Build 8 (1.0.0)

This commit is contained in:
Aiden Vigue 2021-05-22 11:14:07 -04:00
parent b49f796a72
commit ca61e0dd4c
12 changed files with 258 additions and 128 deletions

View File

@ -188,6 +188,7 @@ struct ContentView: View {
@State private var librariesShowRecentlyAdded: [String] = []; @State private var librariesShowRecentlyAdded: [String] = [];
@State private var libraryPrefillID: String = ""; @State private var libraryPrefillID: String = "";
@State private var showSettingsPopover: Bool = false; @State private var showSettingsPopover: Bool = false;
@State private var viewDidLoad: Bool = false;
@Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass? @Environment(\.verticalSizeClass) var verticalSizeClass: UserInterfaceSizeClass?
@Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass? @Environment(\.horizontalSizeClass) var horizontalSizeClass: UserInterfaceSizeClass?
@ -198,6 +199,10 @@ struct ContentView: View {
} }
func startup() { func startup() {
if(_viewDidLoad.wrappedValue) {
return
}
_viewDidLoad.wrappedValue = true;
SentrySDK.start { options in SentrySDK.start { options in
options.dsn = "https://75ac77d6af4d406eb989f3d8ef0f119f@o513670.ingest.sentry.io/5778242" options.dsn = "https://75ac77d6af4d406eb989f3d8ef0f119f@o513670.ingest.sentry.io/5778242"
options.debug = false // Enabled debug when first installing is always helpful options.debug = false // Enabled debug when first installing is always helpful
@ -274,6 +279,18 @@ struct ContentView: View {
_librariesShowRecentlyAdded.wrappedValue = _libraries.wrappedValue.filter { element in _librariesShowRecentlyAdded.wrappedValue = _libraries.wrappedValue.filter { element in
return !array2.contains(element) return !array2.contains(element)
} }
_libraries.wrappedValue.forEach { library in
if(_library_names.wrappedValue[library] == nil) {
_libraries.wrappedValue.removeAll { ele in
if(library == ele) {
return true
} else {
return false
}
}
}
}
} catch { } catch {
} }
@ -301,6 +318,15 @@ struct ContentView: View {
} }
var body: some View { var body: some View {
if(needsToSelectServer) {
NavigationView() {
ConnectToServerView(isActive: $needsToSelectServer)
}
} else if(isSignInErrored) {
NavigationView() {
ConnectToServerView(skip_server: true, skip_server_prefill: globalData.server, reauth_deviceId: globalData.user?.device_uuid ?? "", isActive: $isSignInErrored)
}
} else {
if(!jsi.did) { if(!jsi.did) {
LoadingView(isShowing: $isLoading) { LoadingView(isShowing: $isLoading) {
TabView(selection: $tabSelection) { TabView(selection: $tabSelection) {
@ -375,6 +401,7 @@ struct ContentView: View {
Text("Signing in...") Text("Signing in...")
.onAppear(perform: { .onAppear(perform: {
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
_viewDidLoad.wrappedValue = false
usleep(500000); usleep(500000);
self.jsi.did = false; self.jsi.did = false;
} }
@ -382,6 +409,7 @@ struct ContentView: View {
} }
} }
} }
}
struct ContentView_Previews: PreviewProvider { struct ContentView_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {

View File

@ -10,6 +10,30 @@ import SwiftyRequest
import SwiftyJSON import SwiftyJSON
import SDWebImageSwiftUI import SDWebImageSwiftUI
struct CustomShape: Shape {
let radius: CGFloat
func path(in rect: CGRect) -> Path {
var path = Path()
let tl = CGPoint(x: rect.minX, y: rect.minY)
let tr = CGPoint(x: rect.maxX, y: rect.minY)
let br = CGPoint(x: rect.maxX, y: rect.maxY)
let bls = CGPoint(x: rect.minX + radius, y: rect.maxY)
let blc = CGPoint(x: rect.minX + radius, y: rect.maxY - radius)
path.move(to: tl)
path.addLine(to: tr)
path.addLine(to: br)
path.addLine(to: bls)
path.addRelativeArc(center: blc, radius: radius,
startAngle: Angle.degrees(90), delta: Angle.degrees(90))
return path
}
}
struct ContinueWatchingView: View { struct ContinueWatchingView: View {
@Environment(\.managedObjectContext) private var viewContext @Environment(\.managedObjectContext) private var viewContext
@EnvironmentObject var globalData: GlobalData @EnvironmentObject var globalData: GlobalData
@ -44,12 +68,18 @@ struct ContinueWatchingView: View {
//portrait; use backdrop instead //portrait; use backdrop instead
itemObj.Image = item["BackdropImageTags"][0].string ?? "" itemObj.Image = item["BackdropImageTags"][0].string ?? ""
itemObj.ImageType = "Backdrop" itemObj.ImageType = "Backdrop"
if(itemObj.Image == "") {
itemObj.Image = item["ParentBackdropImageTags"][0].string ?? ""
}
itemObj.BlurHash = item["ImageBlurHashes"]["Backdrop"][itemObj.Image].string ?? "" itemObj.BlurHash = item["ImageBlurHashes"]["Backdrop"][itemObj.Image].string ?? ""
} else { } else {
itemObj.Image = item["ImageTags"]["Primary"].string ?? "" itemObj.Image = item["ImageTags"]["Primary"].string ?? ""
itemObj.ImageType = "Primary" itemObj.ImageType = "Primary"
itemObj.BlurHash = item["ImageBlurHashes"]["Primary"][itemObj.Image].string ?? "" itemObj.BlurHash = item["ImageBlurHashes"]["Primary"][itemObj.Image].string ?? ""
} }
itemObj.Name = item["Name"].string ?? "" itemObj.Name = item["Name"].string ?? ""
itemObj.Type = item["Type"].string ?? "" itemObj.Type = item["Type"].string ?? ""
itemObj.IndexNumber = item["IndexNumber"].int ?? nil itemObj.IndexNumber = item["IndexNumber"].int ?? nil
@ -108,9 +138,10 @@ struct ContinueWatchingView: View {
.padding(6), alignment: .topTrailing .padding(6), alignment: .topTrailing
) )
.overlay( .overlay(
RoundedRectangle(cornerRadius: 10, style: .circular) Rectangle()
.fill(Color(red: 172/255, green: 92/255, blue: 195/255).opacity(0.4)) .fill(Color(red: 172/255, green: 92/255, blue: 195/255))
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 180) .mask(CustomShape(radius: 10))
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 7)
.padding(0), alignment: .bottomLeading .padding(0), alignment: .bottomLeading
) )
.shadow(radius: 5) .shadow(radius: 5)
@ -126,9 +157,10 @@ struct ContinueWatchingView: View {
.frame(width: 320, height: 180) .frame(width: 320, height: 180)
.cornerRadius(10) .cornerRadius(10)
.overlay( .overlay(
RoundedRectangle(cornerRadius: 10, style: .circular) Rectangle()
.fill(Color(red: 172/255, green: 92/255, blue: 195/255).opacity(0.4)) .fill(Color(red: 172/255, green: 92/255, blue: 195/255))
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 180) .mask(CustomShape(radius: 10))
.frame(width: CGFloat((item.ItemProgress/100)*320), height: 7)
.padding(0), alignment: .bottomLeading .padding(0), alignment: .bottomLeading
) )
.shadow(radius: 5) .shadow(radius: 5)
@ -137,6 +169,8 @@ struct ContinueWatchingView: View {
.font(.callout) .font(.callout)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(.primary) .foregroundColor(.primary)
.lineLimit(1)
.frame(width: 320, alignment: .leading)
Spacer().frame(height: 5) Spacer().frame(height: 5)
}.padding(.trailing, 5) }.padding(.trailing, 5)
} }

View File

@ -265,7 +265,7 @@ struct EpisodeItemView: View {
.stroke(Color.secondary, lineWidth: 1) .stroke(Color.secondary, lineWidth: 1)
) )
} }
if(fullItem.CommunityRating != "") { if(fullItem.CommunityRating != "0") {
HStack() { HStack() {
Image(systemName: "star").foregroundColor(.secondary) Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline) Text(fullItem.CommunityRating).font(.subheadline)
@ -275,9 +275,8 @@ struct EpisodeItemView: View {
.offset(x: -7, y: 0.7) .offset(x: -7, y: 0.7)
} }
} }
} }.frame(maxWidth: .infinity, alignment: .leading)
}.frame(maxWidth: .infinity, alignment: .leading).offset(x: 0, y: -46).padding(.trailing, 16)
}.offset(x: 0, y: -46).padding(.trailing, 16)
}.offset(x: 16, y: 40) }.offset(x: 16, y: 40)
, alignment: .bottomLeading) , alignment: .bottomLeading)
VStack(alignment: .leading) { VStack(alignment: .leading) {
@ -466,7 +465,7 @@ struct EpisodeItemView: View {
.stroke(Color.secondary, lineWidth: 1) .stroke(Color.secondary, lineWidth: 1)
) )
} }
if(fullItem.CommunityRating != "") { if(fullItem.CommunityRating != "0") {
HStack() { HStack() {
Image(systemName: "star").foregroundColor(.secondary) Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline) Text(fullItem.CommunityRating).font(.subheadline)

View File

@ -20,6 +20,14 @@
<string>$(MARKETING_VERSION)</string> <string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>7</string> <string>7</string>
<key>DTXApplicationID</key>
<string>8c1f6941-ec78-480c-b589-b41aca29a52e</string>
<key>DTXBeaconURL</key>
<string>https://bf64941kgh.bf.dynatrace.com/mbeacon</string>
<key>DTXStartupLoadBalancing</key>
<true/>
<key>DTXUserOptIn</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>
<false/> <false/>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
@ -42,6 +50,8 @@
<array> <array>
<string>armv7</string> <string>armv7</string>
</array> </array>
<key>UIRequiresFullScreen</key>
<true/>
<key>UIStatusBarHidden</key> <key>UIStatusBarHidden</key>
<true/> <true/>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
@ -53,17 +63,8 @@
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>DTXApplicationID</key>
<string>8c1f6941-ec78-480c-b589-b41aca29a52e</string>
<key>DTXBeaconURL</key>
<string>https://bf64941kgh.bf.dynatrace.com/mbeacon</string>
<key>DTXUserOptIn</key>
<true/>
<key>DTXStartupLoadBalancing</key>
<true/>
</dict> </dict>
</plist> </plist>

View File

@ -103,10 +103,17 @@ struct LatestMediaView: View {
.cornerRadius(10) .cornerRadius(10)
.overlay( .overlay(
ZStack { ZStack {
if(item.ItemBadge == 0) {
Image(systemName: "checkmark")
.font(.caption)
.padding(3)
.foregroundColor(.white)
} else {
Text("\(String(item.ItemBadge ?? 0))") Text("\(String(item.ItemBadge ?? 0))")
.font(.caption) .font(.caption)
.padding(3) .padding(3)
.foregroundColor(.white) .foregroundColor(.white)
}
}.background(Color.black) }.background(Color.black)
.opacity(0.8) .opacity(0.8)
.cornerRadius(10.0) .cornerRadius(10.0)

View File

@ -34,6 +34,10 @@ struct LibraryFilterView: View {
@Binding var close: Bool; @Binding var close: Bool;
func onAppear() { func onAppear() {
if(_viewDidLoad.wrappedValue == true) {
return
}
_viewDidLoad.wrappedValue = true;
if(_output.wrappedValue.contains("&Filters=IsUnplayed")) { if(_output.wrappedValue.contains("&Filters=IsUnplayed")) {
_onlyUnplayed.wrappedValue = true; _onlyUnplayed.wrappedValue = true;
} }
@ -55,10 +59,6 @@ struct LibraryFilterView: View {
_sortOrder.wrappedValue = sortOrder; _sortOrder.wrappedValue = sortOrder;
recalculateFilters() recalculateFilters()
if(_viewDidLoad.wrappedValue == true) {
return
}
_viewDidLoad.wrappedValue = true;
_allGenres.wrappedValue = [] _allGenres.wrappedValue = []
let url = "/Items/Filters?UserId=\(globalData.user?.user_id ?? "")&ParentId=\(library)" let url = "/Items/Filters?UserId=\(globalData.user?.user_id ?? "")&ParentId=\(library)"
let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url) let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + url)
@ -98,6 +98,7 @@ struct LibraryFilterView: View {
} }
func recalculateFilters() { func recalculateFilters() {
print("recalcFilters running");
output = ""; output = "";
if(_onlyUnplayed.wrappedValue) { if(_onlyUnplayed.wrappedValue) {
output = "&Filters=IsUnPlayed"; output = "&Filters=IsUnPlayed";

View File

@ -32,7 +32,7 @@ struct LibrarySearchView: View {
func onAppear() { func onAppear() {
_isLoading.wrappedValue = true; _isLoading.wrappedValue = true;
_items.wrappedValue = []; _items.wrappedValue = [];
let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + _url.wrappedValue + "&searchTerm=" + searchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!) let request = RestRequest(method: .get, url: (globalData.server?.baseURI ?? "") + _url.wrappedValue + "&searchTerm=" + searchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! + (_url.wrappedValue.contains("SortBy") ? "" : "&SortBy=Name&SortOrder=Descending"))
request.headerParameters["X-Emby-Authorization"] = globalData.authHeader request.headerParameters["X-Emby-Authorization"] = globalData.authHeader
request.contentType = "application/json" request.contentType = "application/json"
request.acceptType = "application/json" request.acceptType = "application/json"
@ -151,10 +151,17 @@ struct LibrarySearchView: View {
.frame(width:100, height: 150) .frame(width:100, height: 150)
.cornerRadius(10).overlay( .cornerRadius(10).overlay(
ZStack { ZStack {
if(item.ItemBadge == 0) {
Image(systemName: "checkmark")
.font(.caption)
.padding(3)
.foregroundColor(.white)
} else {
Text("\(String(item.ItemBadge ?? 0))") Text("\(String(item.ItemBadge ?? 0))")
.font(.caption) .font(.caption)
.padding(3) .padding(3)
.foregroundColor(.white) .foregroundColor(.white)
}
}.background(Color.black) }.background(Color.black)
.opacity(0.8) .opacity(0.8)
.cornerRadius(10.0) .cornerRadius(10.0)

View File

@ -71,7 +71,7 @@ struct LibraryView: View {
_library_names.wrappedValue["favorites"] = "Favorites" _library_names.wrappedValue["favorites"] = "Favorites"
_library_ids.wrappedValue.append("genres") _library_ids.wrappedValue.append("genres")
_library_names.wrappedValue["genres"] = "Genres" _library_names.wrappedValue["genres"] = "Genres - WIP"
} }
} }
@ -196,10 +196,17 @@ struct LibraryView: View {
.frame(width:100, height: 150) .frame(width:100, height: 150)
.cornerRadius(10).overlay( .cornerRadius(10).overlay(
ZStack { ZStack {
if(item.ItemBadge == 0) {
Image(systemName: "checkmark")
.font(.caption)
.padding(3)
.foregroundColor(.white)
} else {
Text("\(String(item.ItemBadge ?? 0))") Text("\(String(item.ItemBadge ?? 0))")
.font(.caption) .font(.caption)
.padding(3) .padding(3)
.foregroundColor(.white) .foregroundColor(.white)
}
}.background(Color.black) }.background(Color.black)
.opacity(0.8) .opacity(0.8)
.cornerRadius(10.0) .cornerRadius(10.0)

View File

@ -312,7 +312,7 @@ struct MovieItemView: View {
.stroke(Color.secondary, lineWidth: 1) .stroke(Color.secondary, lineWidth: 1)
) )
} }
if(fullItem.CommunityRating != "") { if(fullItem.CommunityRating != "0") {
HStack() { HStack() {
Image(systemName: "star").foregroundColor(.secondary) Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline) Text(fullItem.CommunityRating).font(.subheadline)
@ -322,8 +322,7 @@ struct MovieItemView: View {
.offset(x: -7, y: 0.7) .offset(x: -7, y: 0.7)
} }
} }
} }.frame(maxWidth: .infinity, alignment: .leading)
}.offset(x: 0, y: -46).padding(.trailing, 16) }.offset(x: 0, y: -46).padding(.trailing, 16)
}.offset(x: 16, y: 40) }.offset(x: 16, y: 40)
, alignment: .bottomLeading) , alignment: .bottomLeading)
@ -512,7 +511,7 @@ struct MovieItemView: View {
.stroke(Color.secondary, lineWidth: 1) .stroke(Color.secondary, lineWidth: 1)
) )
} }
if(fullItem.CommunityRating != "") { if(fullItem.CommunityRating != "0") {
HStack() { HStack() {
Image(systemName: "star").foregroundColor(.secondary) Image(systemName: "star").foregroundColor(.secondary)
Text(fullItem.CommunityRating).font(.subheadline) Text(fullItem.CommunityRating).font(.subheadline)
@ -523,9 +522,9 @@ struct MovieItemView: View {
} }
} }
Spacer() Spacer()
}.frame(maxWidth: .infinity) }.frame(maxWidth: .infinity, alignment: .leading)
.offset(x: 14) .offset(x: 14)
}.frame(maxWidth: .infinity) }.frame(maxWidth: .infinity, alignment: .leading)
Spacer() Spacer()
HStack() { HStack() {
Button() { Button() {

View File

@ -218,10 +218,12 @@ struct SeasonItemView: View {
.foregroundColor(.primary) .foregroundColor(.primary)
.fixedSize(horizontal: false, vertical: true) .fixedSize(horizontal: false, vertical: true)
.offset(y: -4) .offset(y: -4)
if(fullItem.ProductionYear != 0) {
Text(String(fullItem.ProductionYear)).font(.subheadline) Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium) .fontWeight(.medium)
.foregroundColor(.secondary) .foregroundColor(.secondary)
.lineLimit(1) .lineLimit(1)
}
}.offset(x: 0, y: 45) }.offset(x: 0, y: 45)
}.offset(x: 16, y: 22) }.offset(x: 16, y: 22)
, alignment: .bottomLeading) , alignment: .bottomLeading)
@ -324,9 +326,11 @@ struct SeasonItemView: View {
.frame(width: 120, height: 180) .frame(width: 120, height: 180)
.cornerRadius(10) .cornerRadius(10)
Spacer().frame(height: 4) Spacer().frame(height: 4)
if(fullItem.ProductionYear != 0) {
Text(String(fullItem.ProductionYear)).font(.subheadline) Text(String(fullItem.ProductionYear)).font(.subheadline)
.fontWeight(.medium) .fontWeight(.medium)
.foregroundColor(.secondary) .foregroundColor(.secondary)
}
Spacer() Spacer()
} }
ScrollView() { ScrollView() {

View File

@ -43,6 +43,11 @@ struct SeriesItemView: View {
itemObj.ProductionYear = item["ProductionYear"].int ?? 0 itemObj.ProductionYear = item["ProductionYear"].int ?? 0
itemObj.ItemBadge = item["UserData"]["UnplayedItemCount"].int ?? 0 itemObj.ItemBadge = item["UserData"]["UnplayedItemCount"].int ?? 0
itemObj.Image = item["ImageTags"]["Primary"].string ?? "" itemObj.Image = item["ImageTags"]["Primary"].string ?? ""
if(itemObj.Image == "") {
itemObj.Image = item["ParentBackdropImageTags"][0].string ?? ""
}
itemObj.ImageType = "Primary" itemObj.ImageType = "Primary"
itemObj.SeasonImage = item["ParentBackdropImageTags"][0].string ?? "" itemObj.SeasonImage = item["ParentBackdropImageTags"][0].string ?? ""
itemObj.SeasonImageType = "Backdrop" itemObj.SeasonImageType = "Backdrop"
@ -93,10 +98,17 @@ struct SeriesItemView: View {
.cornerRadius(10) .cornerRadius(10)
}.overlay( }.overlay(
ZStack { ZStack {
if(item.ItemBadge == 0) {
Image(systemName: "checkmark")
.font(.caption)
.padding(3)
.foregroundColor(.white)
} else {
Text("\(String(item.ItemBadge ?? 0))") Text("\(String(item.ItemBadge ?? 0))")
.font(.caption) .font(.caption)
.padding(3) .padding(3)
.foregroundColor(.white) .foregroundColor(.white)
}
}.background(Color.black) }.background(Color.black)
.opacity(0.8) .opacity(0.8)
.cornerRadius(10.0) .cornerRadius(10.0)

31
Release Notes.rtf Normal file
View File

@ -0,0 +1,31 @@
{\rtf1\ansi\ansicpg1252\cocoartf2580
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica-Bold;\f1\fswiss\fcharset0 Helvetica;}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
{\*\listtable{\list\listtemplateid1\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{disc\}}{\leveltext\leveltemplateid1\'01\uc0\u8226 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid1}
{\list\listtemplateid2\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace360\levelindent0{\*\levelmarker \{hyphen\}}{\leveltext\leveltemplateid101\'01\uc0\u8259 ;}{\levelnumbers;}\fi-360\li720\lin720 }{\listname ;}\listid2}}
{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}{\listoverride\listid2\listoverridecount0\ls2}}
\margl1440\margr1440\vieww11520\viewh8400\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
\f0\b\fs26 \cf0 1.0.0 (Build: 8)
\f1\b0\fs24 \
\
\pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural\partightenfactor0
\ls1\ilvl0\cf0 {\listtext \uc0\u8226 }Fix progress bar on ContinueWatchingView\
{\listtext \uc0\u8226 }Sort search if no sorting active\
{\listtext \uc0\u8226 }Limit text length on ContinueWatchingView\
{\listtext \uc0\u8226 }Fix \'930\'94 for some production years on SeasonItemView\
{\listtext \uc0\u8226 }Fix \'930\'94 for some CommunityRatings on EpisodeItemView & MovieItemView\
{\listtext \uc0\u8226 }Fix placeholder image for some episodes on ContinueWatchingView\
{\listtext \uc0\u8226 }Fix placeholder image for some seasons w/o their own images in SeriesItemView\
{\listtext \uc0\u8226 }Show checkmarks instead of \'930\'94 on ItemBadges\
{\listtext \uc0\u8226 }Fix having to click twice on selections in LibraryFilterView\
{\listtext \uc0\u8226 }Fix bug where sign in page would disappear on orientation change or app relaunch\
{\listtext \uc0\u8226 }Fix bug where some users would have access to libraries they were not supposed to see.\
{\listtext \uc0\u8226 }Adds Dynatrace session tracking to try and find the cause for random crashes (app is FOOMing)\
{\listtext \uc0\u8226 }Fix memory leak when viewing the main page multiple times\
\pard\tx220\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\li720\fi-720\pardirnatural\partightenfactor0
\ls2\ilvl0\cf0 {\listtext \uc0\u8259 }Squashed some commits in the repo as sensitive tokens were exposed for Sentry & Dynatrace\
{\listtext \uc0\u8259 }Also adds Fastlane\
}