Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions VITTY/VITTY.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
4B37F1E42E02AA7800DCEE5F /* ReminderNotifcationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B37F1E32E02AA6E00DCEE5F /* ReminderNotifcationManager.swift */; };
4B37F1E62E03D7D300DCEE5F /* ExistingHotelView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B37F1E52E03D7D300DCEE5F /* ExistingHotelView.swift */; };
4B37F1E92E04173A00DCEE5F /* SettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B37F1E82E04173500DCEE5F /* SettingsViewModel.swift */; };
4B40E1DA2E27E133004F8447 /* SettingsTololtip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B40E1D92E27E12B004F8447 /* SettingsTololtip.swift */; };
4B40FE5D2E0A917F000BDD07 /* QrCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B40FE5C2E0A9179000BDD07 /* QrCode.swift */; };
4B47CD7B2D7DCB8B00A46FEF /* CreateReminder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B47CD7A2D7DCB8400A46FEF /* CreateReminder.swift */; };
4B5977472DF97D5C009CC224 /* RemainderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5977462DF97D5A009CC224 /* RemainderModel.swift */; };
Expand Down Expand Up @@ -203,6 +204,7 @@
4B37F1E32E02AA6E00DCEE5F /* ReminderNotifcationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderNotifcationManager.swift; sourceTree = "<group>"; };
4B37F1E52E03D7D300DCEE5F /* ExistingHotelView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExistingHotelView.swift; sourceTree = "<group>"; };
4B37F1E82E04173500DCEE5F /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
4B40E1D92E27E12B004F8447 /* SettingsTololtip.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsTololtip.swift; sourceTree = "<group>"; };
4B40FE5C2E0A9179000BDD07 /* QrCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QrCode.swift; sourceTree = "<group>"; };
4B47CD7A2D7DCB8400A46FEF /* CreateReminder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateReminder.swift; sourceTree = "<group>"; };
4B5977462DF97D5A009CC224 /* RemainderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemainderModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -478,6 +480,14 @@
path = ViewModel;
sourceTree = "<group>";
};
4B40E1D82E27E11F004F8447 /* SettingsTooltip */ = {
isa = PBXGroup;
children = (
4B40E1D92E27E12B004F8447 /* SettingsTololtip.swift */,
);
path = SettingsTooltip;
sourceTree = "<group>";
};
4B74D8752E0BF76B00B390E9 /* Components */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -936,6 +946,7 @@
5D1FF2632A32643400B0620A /* Settings */ = {
isa = PBXGroup;
children = (
4B40E1D82E27E11F004F8447 /* SettingsTooltip */,
4B37F1E72E04172E00DCEE5F /* ViewModel */,
5D1FF2642A32643B00B0620A /* View */,
);
Expand Down Expand Up @@ -1169,6 +1180,7 @@
4B40FE5D2E0A917F000BDD07 /* QrCode.swift in Sources */,
4B74D87B2E0BFC7E00B390E9 /* FileUploadHelper.swift in Sources */,
4B183EE82D7C78B600C9D801 /* Courses.swift in Sources */,
4B40E1DA2E27E133004F8447 /* SettingsTololtip.swift in Sources */,
5238C7F12B4AAE8700413946 /* FriendRequestView.swift in Sources */,
528CF1782B769E64007298A0 /* TimeTableAPIService.swift in Sources */,
52D5AB972B6FFC8F00B2E66D /* LoginView.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions VITTY/VITTY/Academics/View/FileUpload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ struct FileUploadView: View {
if !newItems.isEmpty {
uploadImages(newItems)
}
}.onChange(of: capturedImage) { _, newImage in
if let image = newImage {
uploadCapturedImage(image)
}
}
.fileImporter(
isPresented: $showDocumentPicker,
Expand Down
83 changes: 50 additions & 33 deletions VITTY/VITTY/Academics/View/Notes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ struct NoteEditorView: View {
@State private var isInitialized = false
@State private var goback = false

// New state variables for title alert
@State private var showTitleAlert = false
@State private var noteTitle = ""

@Environment(\.modelContext) private var modelContext
let courseCode: String
let courseName: String
Expand All @@ -156,33 +160,38 @@ struct NoteEditorView: View {
self.courseIns = courseIns
self.courseSlot = courseSlot
}

private func handleBackNavigation() {


DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if presentationMode.wrappedValue.isPresented {
presentationMode.wrappedValue.dismiss()
}
}
}
// Check if there are unsaved changes or if it's a new note
if hasUnsavedChanges || (existingNote == nil && !isEmpty) {
showTitleAlert = true
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if presentationMode.wrappedValue.isPresented {
presentationMode.wrappedValue.dismiss()
}
}
}
}

private func initializeContent() {
guard !isInitialized else { return }

if let note = existingNote {
// Pre-populate the title field with existing note name
noteTitle = note.noteName

if let preloaded = preloadedAttributedString {

attributedText = NSMutableAttributedString(attributedString: preloaded)
isEmpty = preloaded.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
isInitialized = true
} else {

Task { @MainActor in
await loadNoteContent(note)
isInitialized = true
}
}
} else {

attributedText = NSMutableAttributedString()
isEmpty = true
isInitialized = true
Expand All @@ -191,14 +200,12 @@ struct NoteEditorView: View {

@MainActor
private func loadNoteContent(_ note: CreateNoteModel) async {

if let cachedAttributedString = note.cachedAttributedString {
attributedText = NSMutableAttributedString(attributedString: cachedAttributedString)
isEmpty = cachedAttributedString.string.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
return
}


do {
guard let data = Data(base64Encoded: note.noteContent) else {
print("Failed to decode base64 data")
Expand All @@ -223,25 +230,26 @@ struct NoteEditorView: View {
}

func saveContent() {
guard hasUnsavedChanges || existingNote == nil else {
handleBackNavigation()
showTitleAlert = true
}

private func saveNoteWithTitle() {
guard !noteTitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
return
}

do {
let data = try NSKeyedArchiver.archivedData(withRootObject: attributedText, requiringSecureCoding: false)
let dataString = data.base64EncodedString()
let title = generateSmartTitle(from: attributedText.string)

if let note = existingNote {
note.noteName = title
note.noteName = noteTitle
note.noteContent = dataString
note.createdAt = Date.now

CreateNoteModel.clearCache()
} else {
let newNote = CreateNoteModel(
noteName: title,
noteName: noteTitle,
userName: authViewModel.loggedInBackendUser?.name ?? "",
courseId: courseCode,
courseName: courseName,
Expand Down Expand Up @@ -285,22 +293,15 @@ struct NoteEditorView: View {

if isInitialized {
VStack {

headerView


textEditorView


toolbarView
}
} else {

ProgressView("Loading...")
.foregroundColor(.white)
}


if showFontPicker {
fontPickerOverlay
}
Expand All @@ -321,6 +322,29 @@ struct NoteEditorView: View {
.navigationBarBackButtonHidden(true)
.animation(.easeInOut(duration: 0.3), value: showFontPicker)
.animation(.easeInOut(duration: 0.3), value: showFontSizePicker)
.alert("Save Note", isPresented: $showTitleAlert) {
TextField("Enter note title", text: $noteTitle)
.textInputAutocapitalization(.words)

Button("Save") {
saveNoteWithTitle()
}
.disabled(noteTitle.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)

Button("Cancel", role: .cancel) {
noteTitle = existingNote?.noteName ?? ""
}

Button("Don't Save", role: .destructive) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
if presentationMode.wrappedValue.isPresented {
presentationMode.wrappedValue.dismiss()
}
}
}
} message: {
Text("Please enter a title for your note.")
}
}

// MARK: - View Components
Expand Down Expand Up @@ -369,7 +393,6 @@ struct NoteEditorView: View {

private var toolbarView: some View {
HStack(spacing: 20) {

Button(action: {
showFontPicker.toggle()
showFontSizePicker = false
Expand All @@ -378,7 +401,6 @@ struct NoteEditorView: View {
.foregroundColor(Color("Accent"))
}


Button(action: {
showFontSizePicker.toggle()
showFontPicker = false
Expand All @@ -393,20 +415,17 @@ struct NoteEditorView: View {
}
}


formatButton(action: toggleBold, icon: "bold", isActive: isBoldActive())
formatButton(action: toggleItalic, icon: "italic", isActive: isItalicActive())
formatButton(action: toggleUnderline, icon: "underline", isActive: isUnderlineActive())


ColorPicker("", selection: $selectedColor, supportsOpacity: false)
.labelsHidden()
.frame(width: 30, height: 30)
.onChange(of: selectedColor) { _, newColor in
applyAttribute(.foregroundColor, value: UIColor(newColor))
}


Button(action: addBulletPoints) {
Image(systemName: "list.bullet")
.foregroundColor(Color("Accent"))
Expand Down Expand Up @@ -505,8 +524,6 @@ struct NoteEditorView: View {
}
}



func addBulletPoints() {
guard selectedRange.length > 0 else { return }

Expand Down
70 changes: 24 additions & 46 deletions VITTY/VITTY/Connect/View/Circles/Components/CirclesRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,24 @@ struct CirclesRow: View {
communityPageViewModel.circleMembers(for: circle.circleID)
}


private var busyCount: Int {
circleMembers.filter {
$0.status != nil && $0.status != "available" && $0.status != "free"
}.count
}


private var availableCount: Int {
circleMembers.filter {
$0.status == nil || $0.status == "available" || $0.status == "free"
}.count
}

private var isLoadingMembers: Bool {
communityPageViewModel.isLoadingCircleMembers(for: circle.circleID)
}

var body: some View {
HStack {

//TODO: left to add a circle image right now its a picsum image


CircleImageView(imageURL: "https://picsum.photos/200/300", size: 48)

Expand All @@ -48,39 +45,29 @@ struct CirclesRow: View {
.font(Font.custom("Poppins-SemiBold", size: 18))
.foregroundColor(Color.white)

if isLoadingMembers {
HStack {
ProgressView()
.scaleEffect(0.7)
Text("Loading...")
.font(Font.custom("Poppins-Regular", size: 12))
.foregroundStyle(Color("Accent"))
}
} else {
HStack {

if busyCount > 0 {
Image("inclass").resizable().frame(width: 20, height: 20)
Text("\(busyCount) busy").foregroundStyle(Color("Accent"))

if availableCount > 0 {
Spacer().frame(width: 20)
}
}
HStack {

if busyCount > 0 {
Image("inclass").resizable().frame(width: 20, height: 20)
Text("\(busyCount) busy").foregroundStyle(Color("Accent"))


if availableCount > 0 {
Image("available").resizable().frame(width: 20, height: 20)
Text("\(availableCount) available").foregroundStyle(Color("Accent"))
}


if circleMembers.isEmpty && !isLoadingMembers {
Text("No members")
.font(Font.custom("Poppins-Regular", size: 12))
.foregroundStyle(Color("Accent").opacity(0.7))
Spacer().frame(width: 20)
}
}


if availableCount > 0 {
Image("available").resizable().frame(width: 20, height: 20)
Text("\(availableCount) available").foregroundStyle(Color("Accent"))
}


if circleMembers.isEmpty {
Text("No members")
.font(Font.custom("Poppins-Regular", size: 12))
.foregroundStyle(Color("Accent").opacity(0.7))
}
}
}
Spacer()
Expand All @@ -90,17 +77,7 @@ struct CirclesRow: View {
RoundedRectangle(cornerRadius: 15)
.fill(Color("Secondary"))
)
.onAppear {

communityPageViewModel.fetchCircleMemberData(
from: "\(APIConstants.base_url)circles/\(circle.circleID)",
token: authViewModel.loggedInBackendUser?.token ?? "",
loading: true,
circleID: circle.circleID
)
}



}


Expand All @@ -114,6 +91,7 @@ struct CirclesRow: View {
return cleanedName
}
}

struct CircleImageView: View {
let imageURL: String
let size: CGFloat
Expand Down
Loading
Loading