From 0432c9799d8c2ca151014114edaa95527faf4d84 Mon Sep 17 00:00:00 2001 From: QuentinArguillere Date: Fri, 29 Mar 2024 14:08:52 +0100 Subject: [PATCH] Start schedule meeting fragment and model --- Linphone.xcodeproj/project.pbxproj | 4 + .../earth.imageset/Contents.json | 21 ++ .../Assets.xcassets/earth.imageset/earth.svg | 1 + .../note.imageset/Contents.json | 21 ++ .../Assets.xcassets/note.imageset/note.svg | 1 + .../Fragments/ScheduleMeetingFragment.swift | 223 ++++++++++++++++++ .../Main/Meetings/Models/MeetingModel.swift | 2 +- .../ViewModel/ScheduleMeetingViewModel.swift | 35 ++- 8 files changed, 286 insertions(+), 22 deletions(-) create mode 100644 Linphone/Assets.xcassets/earth.imageset/Contents.json create mode 100644 Linphone/Assets.xcassets/earth.imageset/earth.svg create mode 100644 Linphone/Assets.xcassets/note.imageset/Contents.json create mode 100644 Linphone/Assets.xcassets/note.imageset/note.svg create mode 100644 Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift diff --git a/Linphone.xcodeproj/project.pbxproj b/Linphone.xcodeproj/project.pbxproj index b287e6ada..684110804 100644 --- a/Linphone.xcodeproj/project.pbxproj +++ b/Linphone.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 6613A0B62BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6613A0B52BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift */; }; 662B69D92B25DE18007118BF /* TelecomManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662B69D82B25DE18007118BF /* TelecomManager.swift */; }; 662B69DB2B25DE25007118BF /* ProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 662B69DA2B25DE25007118BF /* ProviderDelegate.swift */; }; + 6646A7A32BB2E224006B842A /* ScheduleMeetingFragment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6646A7A22BB2E224006B842A /* ScheduleMeetingFragment.swift */; }; 667E5D7F2B8E430C00EBCFC4 /* linphonerc-factory in Resources */ = {isa = PBXBuildFile; fileRef = D732A90B2B0376F500DB42BA /* linphonerc-factory */; }; 667E5D812B8E444E00EBCFC4 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */; }; 6691CA7E2B839C2D00B2A7B8 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */; }; @@ -164,6 +165,7 @@ 6613A0B52BAEBE5C008923A4 /* ScheduleMeetingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMeetingViewModel.swift; sourceTree = ""; }; 662B69D82B25DE18007118BF /* TelecomManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelecomManager.swift; sourceTree = ""; }; 662B69DA2B25DE25007118BF /* ProviderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderDelegate.swift; sourceTree = ""; }; + 6646A7A22BB2E224006B842A /* ScheduleMeetingFragment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleMeetingFragment.swift; sourceTree = ""; }; 667E5D802B8E444D00EBCFC4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 6691CA7D2B839C2D00B2A7B8 /* NotificationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = ""; }; 66C491F82B24D25A00CEA16D /* ConfigExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigExtension.swift; sourceTree = ""; }; @@ -341,6 +343,7 @@ children = ( 6613A0AD2BAEB7DF008923A4 /* MeetingFragment.swift */, 6613A0AF2BAEB7F4008923A4 /* MeetingsListFragment.swift */, + 6646A7A22BB2E224006B842A /* ScheduleMeetingFragment.swift */, ); path = Fragments; sourceTree = ""; @@ -957,6 +960,7 @@ D7B99E992B29B39000BE7BF2 /* CallViewModel.swift in Sources */, D7702EF22AC7205000557C00 /* WelcomeView.swift in Sources */, D732A9152B04C7FE00DB42BA /* HistoryListViewModel.swift in Sources */, + 6646A7A32BB2E224006B842A /* ScheduleMeetingFragment.swift in Sources */, D71FCA7F2AE1397200D2E43E /* ContactsListViewModel.swift in Sources */, D71FCA812AE14CFC00D2E43E /* ContactsListFragment.swift in Sources */, D734499B2BC694C900778C56 /* MeetingWaitingRoomViewModel.swift in Sources */, diff --git a/Linphone/Assets.xcassets/earth.imageset/Contents.json b/Linphone/Assets.xcassets/earth.imageset/Contents.json new file mode 100644 index 000000000..ce928664a --- /dev/null +++ b/Linphone/Assets.xcassets/earth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "earth.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/earth.imageset/earth.svg b/Linphone/Assets.xcassets/earth.imageset/earth.svg new file mode 100644 index 000000000..6bdba5060 --- /dev/null +++ b/Linphone/Assets.xcassets/earth.imageset/earth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/Assets.xcassets/note.imageset/Contents.json b/Linphone/Assets.xcassets/note.imageset/Contents.json new file mode 100644 index 000000000..e7ac1e9ef --- /dev/null +++ b/Linphone/Assets.xcassets/note.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "note.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Linphone/Assets.xcassets/note.imageset/note.svg b/Linphone/Assets.xcassets/note.imageset/note.svg new file mode 100644 index 000000000..a5378aa7a --- /dev/null +++ b/Linphone/Assets.xcassets/note.imageset/note.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift b/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift new file mode 100644 index 000000000..1eea902df --- /dev/null +++ b/Linphone/UI/Main/Meetings/Fragments/ScheduleMeetingFragment.swift @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2010-2024 Belledonne Communications SARL. + * + * This file is part of linphone-iphone + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// swiftlint:disable line_length + +import SwiftUI + +struct ScheduleMeetingFragment: View { + + @ObservedObject private var sharedMainViewModel = SharedMainViewModel.shared + + @Environment(\.dismiss) var dismiss + + private var idiom: UIUserInterfaceIdiom { UIDevice.current.userInterfaceIdiom } + @State private var orientation = UIDevice.current.orientation + + @ObservedObject var scheduleMeetingViewModel: ScheduleMeetingViewModel + + @State private var delayedColor = Color.white + + var body: some View { + ZStack { + VStack(spacing: 16) { + if #available(iOS 16.0, *) { + Rectangle() + .foregroundColor(delayedColor) + .edgesIgnoringSafeArea(.top) + .frame(height: 0) + .task(delayColor) + } else if idiom != .pad && !(orientation == .landscapeLeft || orientation == .landscapeRight + || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) { + Rectangle() + .foregroundColor(delayedColor) + .edgesIgnoringSafeArea(.top) + .frame(height: 1) + .task(delayColor) + } + + HStack { + Image("caret-left") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.orangeMain500) + .frame(width: 25, height: 25, alignment: .leading) + .padding(.all, 10) + .padding(.top, 2) + .padding(.leading, -10) + .onTapGesture { + } + + Text("New meeting" ) + .multilineTextAlignment(.leading) + .default_text_style_orange_800(styleSize: 16) + + Spacer() + } + .frame(maxWidth: .infinity) + .frame(height: 50) + .padding(.horizontal) + .padding(.bottom, 4) + .background(.white) + + HStack(alignment: .center, spacing: 8) { + Image("users-three") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 24, height: 24) + .padding(.leading, 16) + TextField("Subject", text: $scheduleMeetingViewModel.subject) + .default_text_style_700(styleSize: 20) + .frame(height: 29, alignment: .leading) + Spacer() + } + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + + HStack(alignment: .center, spacing: 8) { + Image("clock") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 16) + Text(scheduleMeetingViewModel.fromDateStr) + .fontWeight(.bold) + .frame(width: 300, height: 29, alignment: .leading) + .padding(.leading, 8) + .default_text_style_500(styleSize: 16) + .background(Color.gray200) + Spacer() + } + + HStack(alignment: .center, spacing: 8) { + Text(scheduleMeetingViewModel.fromTime) + .fontWeight(.bold) + .frame(height: 29, alignment: .leading) + .default_text_style_500(styleSize: 16) + .opacity(scheduleMeetingViewModel.allDayMeeting ? 0 : 1) + Text(scheduleMeetingViewModel.toTime) + .fontWeight(.bold) + .frame(height: 29, alignment: .leading) + .default_text_style_500(styleSize: 16) + .opacity(scheduleMeetingViewModel.allDayMeeting ? 0 : 1) + Toggle("", isOn: $scheduleMeetingViewModel.allDayMeeting) + .labelsHidden() + .tint(Color.orangeMain300) + Text("All day") + .fontWeight(.bold) + .default_text_style_500(styleSize: 16) + } + + HStack(alignment: .center, spacing: 8) { + Image("earth") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 16) + Text("TODO : timezone") + .fontWeight(.bold) + .padding(.leading, 8) + .default_text_style_500(styleSize: 16) + Spacer() + } + + HStack(alignment: .center, spacing: 8) { + Image("arrow-clockwise") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c800) + .frame(width: 24, height: 24) + .padding(.leading, 16) + Text("TODO : repeat") + .fontWeight(.bold) + .padding(.leading, 8) + .default_text_style_500(styleSize: 16) + Spacer() + } + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + + HStack(alignment: .center, spacing: 8) { + Image("note") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 24, height: 24) + .padding(.leading, 16) + + TextField("Add a description", text: $scheduleMeetingViewModel.subject) + .default_text_style_700(styleSize: 16) + .frame(height: 29, alignment: .leading) + Spacer() + } + + Rectangle() + .foregroundStyle(.clear) + .frame(height: 1) + .background(Color.gray200) + HStack(alignment: .center, spacing: 8) { + Image("users") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.grayMain2c600) + .frame(width: 24, height: 24) + .padding(.leading, 16) + + Text("Add participants") + .default_text_style_700(styleSize: 16) + .frame(height: 29, alignment: .leading) + Spacer() + } + ScrollView { + + } + .background(Color.gray100) + } + .background(.white) + .navigationBarHidden(true) + } + } + + @Sendable private func delayColor() async { + try? await Task.sleep(nanoseconds: 250_000_000) + delayedColor = Color.orangeMain500 + } + + func delayColorDismiss() { + Task { + try? await Task.sleep(nanoseconds: 80_000_000) + delayedColor = .white + } + } +} + +#Preview { + ScheduleMeetingFragment(scheduleMeetingViewModel: ScheduleMeetingViewModel()) +} + +// swiftlint:enable line_length diff --git a/Linphone/UI/Main/Meetings/Models/MeetingModel.swift b/Linphone/UI/Main/Meetings/Models/MeetingModel.swift index a6c797916..4b3baa492 100644 --- a/Linphone/UI/Main/Meetings/Models/MeetingModel.swift +++ b/Linphone/UI/Main/Meetings/Models/MeetingModel.swift @@ -34,7 +34,7 @@ class MeetingModel: ObservableObject { let formatter = DateFormatter() formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" startTime = formatter.string(from: meetingDate) - let endDate = Calendar.current.date(byAdding: .minute, value: Int(confInfo.duration), to: meetingDate) + let endDate = Calendar.current.date(byAdding: .minute, value: Int(confInfo.duration), to: meetingDate)! endTime = formatter.string(from: endDate) time = "\(startTime) - \(endTime)" diff --git a/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift b/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift index 9c42bc6ef..aa04c0ab9 100644 --- a/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift +++ b/Linphone/UI/Main/Meetings/ViewModel/ScheduleMeetingViewModel.swift @@ -21,24 +21,23 @@ import Foundation import linphonesw import Combine - class ScheduleMeetingViewModel: ObservableObject { private let TAG = "[ScheduleMeetingViewModel]" - @Published var isBroadcastSelected: Bool - @Published var showBroadcastHelp: Bool - @Published var subject: String - @Published var description: String - @Published var allDayMeeting: Bool - @Published var fromDateStr: String - @Published var fromTime: String - @Published var toDateStr: String - @Published var toTime: String - @Published var timezone: String - @Published var sendInvitations: Bool + @Published var isBroadcastSelected: Bool = false + @Published var showBroadcastHelp: Bool = false + @Published var subject: String = "" + @Published var description: String = "" + @Published var allDayMeeting: Bool = false + @Published var fromDateStr: String = "" + @Published var fromTime: String = "" + @Published var toDateStr: String = "" + @Published var toTime: String = "" + @Published var timezone: String = "" + @Published var sendInvitations: Bool = true // var participants = MutableLiveData>() - @Published var operationInProgress: Bool - @Published var conferenceCreatedEvent: Bool + @Published var operationInProgress: Bool = false + @Published var conferenceCreatedEvent: Bool = false var conferenceScheduler: ConferenceScheduler? var conferenceInfoToEdit: ConferenceScheduler? @@ -47,11 +46,6 @@ class ScheduleMeetingViewModel: ObservableObject { private var toDate: Date init() { - isBroadcastSelected = false - showBroadcastHelp = false - allDayMeeting = false - sendInvitations = true - fromDate = Calendar.current.date(byAdding: .hour, value: 1, to: Date.now)! toDate = Calendar.current.date(byAdding: .hour, value: 1, to: fromDate)! @@ -78,11 +72,10 @@ class ScheduleMeetingViewModel: ObservableObject { let formatter = DateFormatter() formatter.dateFormat = Locale.current.identifier == "fr_FR" ? "HH:mm" : "h:mm a" fromTime = formatter.string(from: fromDate) - toTime = formatter.string(from:toDate) + toTime = formatter.string(from: toDate) } private func updateTimezone() { // TODO } } -