Skip to content
Open
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
27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,21 @@
</p>

---

[![Join Us](https://img.shields.io/badge/Join%20Us-Developer%20Student%20Clubs-red)](https://dsc.community.dev/vellore-institute-of-technology/)
[![Discord Chat](https://img.shields.io/discord/760928671698649098.svg)](https://discord.gg/498KVdSKWR)

[![DOCS](https://img.shields.io/badge/Documentation-see%20docs-green?style=flat-square&logo=appveyor)](INSERT_LINK_FOR_DOCS_HERE)
[![UI ](https://img.shields.io/badge/User%20Interface-Link%20to%20UI-orange?style=flat-square&logo=appveyor)](INSERT_UI_LINK_HERE)
[![UI ](https://img.shields.io/badge/User%20Interface-Link%20to%20UI-orange?style=flat-square&logo=appveyor)](INSERT_UI_LINK_HERE)

<br>


## Running

Open in Xcode and run the app in simulator.

## Contributors

<table>
<tr align="center">
<td>
<table align="center">
<tr align="center" style="text-align: center;">
<td style="text-align: center;">
Chandram Dutta
<p align="center">
<img src = "https://dscvit.com/images/dsc-logo-square.svg" width="150" height="150" alt="Chandram Dutta">
Expand All @@ -38,6 +35,20 @@ Open in Xcode and run the app in simulator.
</a>
</p>
</td>
<td style="text-align: center;">
Rujin Devkota
<p align="center">
<img src = "https://i.postimg.cc/0Nj3rH28/IMG-3710.avif" width="150" height="150" alt="Rujin Devkota">
</p>
<p align="center">
<a href = "https://github.com/rujin2003">
<img src = "http://www.iconninja.com/files/241/825/211/round-collaboration-social-github-code-circle-network-icon.svg" width="36" height = "36" alt="GitHub"/>
</a>
<a href = "https://www.linkedin.com/in/rujin-devkota">
<img src = "http://www.iconninja.com/files/863/607/751/network-linkedin-social-connection-circular-circle-media-icon.svg" width="36" height="36" alt="LinkedIn"/>
</a>
</p>
</td>
</tr>
</table>

Expand Down
36 changes: 36 additions & 0 deletions VITTY/GoogleService-Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CLIENT_ID</key>
<string>272763363329-i8n51oo9m30h9it7qq9ufmd0lahnmm63.apps.googleusercontent.com</string>
<key>REVERSED_CLIENT_ID</key>
<string>com.googleusercontent.apps.272763363329-i8n51oo9m30h9it7qq9ufmd0lahnmm63</string>
<key>ANDROID_CLIENT_ID</key>
<string>272763363329-143lqjkb0i5a75lc0iglc26jlb61po0c.apps.googleusercontent.com</string>
<key>API_KEY</key>
<string>AIzaSyCJYYDMdzQiNiY0pxqbrglEw85BSlGgHBc</string>
<key>GCM_SENDER_ID</key>
<string>272763363329</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.gdscvit.vittyios</string>
<key>PROJECT_ID</key>
<string>vitty-dscvit</string>
<key>STORAGE_BUCKET</key>
<string>vitty-dscvit.appspot.com</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:272763363329:ios:3b020b67f7527e83e2e000</string>
</dict>
</plist>
5 changes: 5 additions & 0 deletions VITTY/VITTY.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
4BD63D742D70547E00EEF5D7 /* EmptyClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD63D722D70547E00EEF5D7 /* EmptyClass.swift */; };
4BD63D772D70610B00EEF5D7 /* EmptyClassAPIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD63D762D70610000EEF5D7 /* EmptyClassAPIService.swift */; };
4BD63D7A2D70636400EEF5D7 /* EmptyClassRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BD63D792D70635D00EEF5D7 /* EmptyClassRoomViewModel.swift */; };
4BD77EA22EBBCE050027F234 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4BD77EA12EBBCE050027F234 /* GoogleService-Info.plist */; };
4BF03C992D7819E30098C803 /* Notes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF03C982D7819E00098C803 /* Notes.swift */; };
4BF03C9B2D7838C80098C803 /* NotesHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF03C9A2D7838C50098C803 /* NotesHelper.swift */; };
4BF0C77B2D93108B00016202 /* SearchBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF0C77A2D93108500016202 /* SearchBar.swift */; };
Expand Down Expand Up @@ -241,6 +242,7 @@
4BD63D722D70547E00EEF5D7 /* EmptyClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyClass.swift; sourceTree = "<group>"; };
4BD63D762D70610000EEF5D7 /* EmptyClassAPIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyClassAPIService.swift; sourceTree = "<group>"; };
4BD63D792D70635D00EEF5D7 /* EmptyClassRoomViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyClassRoomViewModel.swift; sourceTree = "<group>"; };
4BD77EA12EBBCE050027F234 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
4BF03C982D7819E00098C803 /* Notes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notes.swift; sourceTree = "<group>"; };
4BF03C9A2D7838C50098C803 /* NotesHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotesHelper.swift; sourceTree = "<group>"; };
4BF0C77A2D93108500016202 /* SearchBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchBar.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -418,6 +420,7 @@
children = (
4BC853C52DF6F71B0092B2E2 /* VittyWidgetExtension.entitlements */,
4B1A500E2E3E61530060314D /* GoogleService-Info.plist */,
4BD77EA12EBBCE050027F234 /* GoogleService-Info.plist */,
4B8FB2C72E39D29F00E50AE2 /* GoogleService-Info.plist */,
5251A7FF2B46E3C000D44CFE /* .swift-format */,
314A408E27383BEC0058082F /* VITTYApp.swift */,
Expand Down Expand Up @@ -1127,6 +1130,7 @@
31128CFA2772F57E0084C9EA /* Poppins-SemiBoldItalic.ttf in Resources */,
31128CFC2772F57E0084C9EA /* Poppins-Regular.ttf in Resources */,
4B8FB2C82E39D29F00E50AE2 /* GoogleService-Info.plist in Resources */,
4BD77EA22EBBCE050027F234 /* GoogleService-Info.plist in Resources */,
314A409627383BEE0058082F /* Preview Assets.xcassets in Resources */,
314A409327383BEE0058082F /* Assets.xcassets in Resources */,
);
Expand Down Expand Up @@ -1437,6 +1441,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"VITTY/Preview Content\"";
DEVELOPMENT_TEAM = C7RX29D33F;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = VITTY/Info.plist;
Expand Down
88 changes: 73 additions & 15 deletions VITTY/VITTY/Academics/VIewModel/ReminderNotifcationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,39 +24,97 @@ class NotificationManager {
}

func scheduleNotification(title: String, body: String, date: Date, identifier: String) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default
// Check if notifications are enabled
guard UserDefaults.standard.bool(forKey: "notificationsEnabled") else {
print("Notifications are disabled. Skipping reminder notification scheduling.")
return
}

// Check if date is in the past
guard date > Date() else {
print("Cannot schedule notification for past date: \(date)")
return
}

// Check notification authorization
UNUserNotificationCenter.current().getNotificationSettings { settings in
guard settings.authorizationStatus == .authorized else {
print("Notification authorization not granted. Cannot schedule reminder.")
return
}

let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default

let triggerDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)
let triggerDate = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: date)
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDate, repeats: false)

let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Failed to schedule notification: \(error)")
UNUserNotificationCenter.current().add(request) { error in
if let error = error {
print("Failed to schedule notification: \(error)")
} else {
print("Successfully scheduled reminder notification: '\(title)' at \(date)")
}
}
}
}

func scheduleReminderNotifications(title: String, date: Date,subject: String) {

func scheduleReminderNotifications(title: String, date: Date, subject: String) {
// Check if notifications are enabled
guard UserDefaults.standard.bool(forKey: "notificationsEnabled") else {
print("Notifications are disabled. Skipping reminder notification scheduling.")
return
}

// Check if date is in the past
guard date > Date() else {
print("Cannot schedule reminder notifications for past date: \(date)")
return
}

// Create unique identifier using title, subject, and timestamp
let timestamp = Int(date.timeIntervalSince1970)
let baseIdentifier = "reminder-\(subject)-\(title)-\(timestamp)"

// Schedule exact time notification
scheduleNotification(
title: "\(subject): \(title)",
body: "Your scheduled reminder is now.",
date: date,
identifier: "\(title)-exact"
identifier: "\(baseIdentifier)-exact"
)

// Calculate 10 minutes before, handling edge cases safely
guard let tenMinutesBefore = Calendar.current.date(byAdding: .minute, value: -10, to: date),
tenMinutesBefore > Date() else {
print("Cannot schedule 10-minute reminder notification (too close to current time or in the past)")
// Still schedule the exact time notification
return
}

let tenMinutesBefore = Calendar.current.date(byAdding: .minute, value: -10, to: date)!
scheduleNotification(
title: "Upcoming Reminder: \(title)",
body: "Your reminder is in 10 minutes.",
date: tenMinutesBefore,
identifier: "\(title)-early"
identifier: "\(baseIdentifier)-early"
)
}

/// Cancel notifications for a specific reminder
func cancelReminderNotifications(title: String, date: Date, subject: String) {
let timestamp = Int(date.timeIntervalSince1970)
let baseIdentifier = "reminder-\(subject)-\(title)-\(timestamp)"

let identifiers = [
"\(baseIdentifier)-exact",
"\(baseIdentifier)-early"
]

UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: identifiers)
print("Cancelled reminder notifications for: \(title)")
}
}
81 changes: 9 additions & 72 deletions VITTY/VITTY/Connect/View/Circles/Components/JoinGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,21 @@ struct JoinGroup: View {

var body: some View {
ZStack {
VStack(spacing: 20) {
VStack(spacing: 0) {
// Drag indicator
Capsule()
.fill(Color.gray.opacity(0.5))
.frame(width: 50, height: 5)
.padding(.top, 10)
.padding(.top, 8)

Spacer().frame(height: 7)
// Title
Text("Join Circle")
.font(.system(size: 21, weight: .bold))
.foregroundColor(.white)
.padding(.top, 20)
.padding(.bottom, 24)

Spacer().frame(width: 20)

// Input section
VStack(alignment: .leading, spacing: 10) {
Text("Enter circle code")
.font(.system(size: 16, weight: .bold))
Expand All @@ -63,54 +65,9 @@ struct JoinGroup: View {
}
.padding(.horizontal, 20)

HStack {
Rectangle()
.fill(Color.gray.opacity(0.5))
.frame(height: 1)
Text("OR")
.font(.system(size: 16, weight: .bold))
.foregroundColor(.white)
.padding(.horizontal, 10)
Rectangle()
.fill(Color.gray.opacity(0.5))
.frame(height: 1)
}
.padding(.horizontal, 20)

HStack {
Text("Scan QR Code")
.font(.system(size: 16, weight: .bold))
.foregroundColor(Color("Accent"))
.padding(.leading, 20)
Spacer()
}

Button(action: {
openCameraApp()
}) {
VStack {
Image(systemName: "qrcode.viewfinder")
.resizable()
.scaledToFit()
.frame(width: 100, height: 100)
.foregroundColor(Color.white)

Text("Tap to open camera")
.font(.system(size: 14))
.foregroundColor(.gray)
}
.frame(width: screenWidth * 0.8, height: screenHeight * 0.25)
.background(Color.black.opacity(0.3))
.cornerRadius(12)
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color.gray.opacity(0.5), lineWidth: 1)
)
}
.disabled(isJoining)

Spacer()

// Join button
HStack {
Spacer()
Button(action: {
Expand All @@ -135,7 +92,7 @@ struct JoinGroup: View {
}
.padding(.bottom, 20)
}
.presentationDetents([.height(screenHeight * 0.65)])
.presentationDetents([.height(screenHeight * 0.35)])
.background(Color("Secondary"))

if showToast {
Expand Down Expand Up @@ -174,26 +131,6 @@ struct JoinGroup: View {
}
}

// MARK: - Open Camera App

private func openCameraApp() {
guard let url = URL(string: "camera:") else {
showToast(message: "Camera not available", isError: true)
return
}

if UIApplication.shared.canOpenURL(url) {
UIApplication.shared.open(url)
} else {

if let settingsURL = URL(string: UIApplication.openSettingsURLString) {
showToast(message: "Please open Camera app manually", isError: false)
} else {
showToast(message: "Camera app not available", isError: true)
}
}
}

// MARK: - Handle Deep Link

private func handleDeepLink(_ url: URL) {
Expand Down
6 changes: 3 additions & 3 deletions VITTY/VITTY/Home/View/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ struct HomeView: View {
ZStack {
if !showProfileSidebar {
Button {
withAnimation(.easeInOut(duration: 0.8)) {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
showProfileSidebar = true
}
} label: {
Expand Down Expand Up @@ -455,7 +455,7 @@ struct HomeView: View {
.ignoresSafeArea()
.transition(.opacity)
.onTapGesture {
withAnimation(.easeInOut(duration: 0.8)) {
withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
showProfileSidebar = false
}
}
Expand All @@ -468,7 +468,7 @@ struct HomeView: View {
showLogoutAlert: $showLogoutAlert
)
.frame(width: UIScreen.main.bounds.width * 0.75)
.transition(.move(edge: .trailing))
.transition(.move(edge: .trailing).combined(with: .opacity))
}
}
}
Expand Down
Loading
Loading