Compare commits
No commits in common. "c66ea52abf01a4199a6c0037c7211d13983f2c80" and "4e4016533ef17e583104fbf6070bc691b49ea731" have entirely different histories.
c66ea52abf
...
4e4016533e
14
.gitignore
vendored
Normal file
14
.gitignore
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
.DS_Store
|
||||
._.DS_Store
|
||||
**/.DS_Store
|
||||
**/._.DS_Store
|
||||
*.dSYM
|
||||
.vscode/
|
||||
just_code/**/*.txt
|
||||
temp/
|
||||
java/Main.java
|
||||
**/*.class
|
||||
**/package-lock.json
|
||||
**/package.json
|
||||
**/tsconfig.json
|
||||
node_modules/
|
||||
21
CLAUDE.md
21
CLAUDE.md
@ -1,21 +0,0 @@
|
||||
# LemonLimeTracker Project Guidelines
|
||||
|
||||
## Core Rules & Constraints
|
||||
- **NO Paid Entitlements**: NEVER add CloudKit, iCloud sync, App Groups, or Remote Notifications to Xcode Signing & Capabilities. Avoid build errors.
|
||||
- **Persistence**: Use strictly local SwiftData. Do NOT use `NSPersistentCloudKitContainer`.
|
||||
|
||||
## Architecture & Future Scope
|
||||
- **Pattern**: Clean Architecture + MVVM.
|
||||
- **Repository Pattern**: Strict separation of Data processing (SwiftData) and UI Views to allow easy future integration of CloudKit, Apple Watch, and Widgets.
|
||||
- **Folder Structure**:
|
||||
- `IOS/Models`: SwiftData Models (Task, Category, Goal, Log).
|
||||
- `IOS/Repositories`: Data access layer (Protocols + Implementations).
|
||||
- `IOS/ViewModels`: Business logic (`@Observable`).
|
||||
- `IOS/Views`: SwiftUI Views.
|
||||
- `IOS/Core`: App entry point, Constants, ActivityKit.
|
||||
- `IOS/Resources`: Assets, Info.plist, Localizable.strings.
|
||||
|
||||
## UI / Design System
|
||||
- **Light Mode**: Green, Yellow, White.
|
||||
- **Dark Mode**: Green, Yellow, Black.
|
||||
- **Localization**: Default is Korean. Support English.
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// LemonLimeTracker
|
||||
//
|
||||
// Created by 송예찬 on 6/18/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
var body: some View {
|
||||
VStack {
|
||||
Image(systemName: "globe")
|
||||
.imageScale(.large)
|
||||
.foregroundStyle(.tint)
|
||||
Text("Hello, world!")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
enum Theme {
|
||||
static let green = Color(red: 0.18, green: 0.72, blue: 0.32)
|
||||
static let yellow = Color(red: 0.96, green: 0.86, blue: 0.12)
|
||||
|
||||
static let background = Color(UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark ? .black : .white
|
||||
})
|
||||
|
||||
static let surface = Color(UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark
|
||||
? UIColor(red: 0.10, green: 0.10, blue: 0.10, alpha: 1)
|
||||
: UIColor(red: 0.97, green: 0.97, blue: 0.97, alpha: 1)
|
||||
})
|
||||
|
||||
static let primaryText = Color(UIColor { traits in
|
||||
traits.userInterfaceStyle == .dark ? .white : .black
|
||||
})
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
#if os(iOS)
|
||||
import ActivityKit
|
||||
import Foundation
|
||||
|
||||
struct TimerActivityAttributes: ActivityAttributes {
|
||||
struct ContentState: Codable, Hashable {
|
||||
var startDate: Date
|
||||
var elapsedSeconds: Int
|
||||
}
|
||||
|
||||
var taskName: String
|
||||
var taskIcon: String
|
||||
}
|
||||
#endif
|
||||
@ -1,19 +0,0 @@
|
||||
//
|
||||
// LemonLimeTrackerApp.swift
|
||||
// LemonLimeTracker
|
||||
//
|
||||
// Created by 송예찬 on 6/18/26.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
@main
|
||||
struct LemonLimeTrackerApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RootView()
|
||||
}
|
||||
.modelContainer(SwiftDataService.shared.container)
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
@Model
|
||||
final class Category {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var name: String
|
||||
var colorHex: String
|
||||
|
||||
@Relationship(deleteRule: .cascade, inverse: \TaskItem.category)
|
||||
var tasks: [TaskItem] = []
|
||||
|
||||
init(id: UUID = UUID(), name: String, colorHex: String) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.colorHex = colorHex
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
enum GoalFrequency: String, Codable {
|
||||
case daily
|
||||
case weekly
|
||||
case monthly
|
||||
}
|
||||
|
||||
@Model
|
||||
final class Goal {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var targetType: String // "count" or "duration"
|
||||
var conditions: Double // threshold value to meet the goal
|
||||
var frequency: GoalFrequency
|
||||
|
||||
var task: TaskItem?
|
||||
var category: Category?
|
||||
|
||||
init(
|
||||
id: UUID = UUID(),
|
||||
targetType: String,
|
||||
conditions: Double,
|
||||
frequency: GoalFrequency,
|
||||
task: TaskItem? = nil,
|
||||
category: Category? = nil
|
||||
) {
|
||||
self.id = id
|
||||
self.targetType = targetType
|
||||
self.conditions = conditions
|
||||
self.frequency = frequency
|
||||
self.task = task
|
||||
self.category = category
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
enum TaskType: String, Codable {
|
||||
case count
|
||||
case time
|
||||
}
|
||||
|
||||
@Model
|
||||
final class TaskItem {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var name: String
|
||||
var icon: String
|
||||
var type: TaskType
|
||||
|
||||
var category: Category?
|
||||
|
||||
@Relationship(deleteRule: .cascade, inverse: \TaskLog.task)
|
||||
var logs: [TaskLog] = []
|
||||
|
||||
init(id: UUID = UUID(), name: String, icon: String, type: TaskType, category: Category? = nil) {
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.icon = icon
|
||||
self.type = type
|
||||
self.category = category
|
||||
}
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
@Model
|
||||
final class TaskLog {
|
||||
@Attribute(.unique) var id: UUID
|
||||
var date: Date
|
||||
var duration: TimeInterval
|
||||
var count: Int
|
||||
|
||||
var task: TaskItem?
|
||||
|
||||
init(id: UUID = UUID(), date: Date = .now, duration: TimeInterval = 0, count: Int = 0) {
|
||||
self.id = id
|
||||
self.date = date
|
||||
self.duration = duration
|
||||
self.count = count
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftData
|
||||
|
||||
@MainActor
|
||||
final class SwiftDataService {
|
||||
static let shared = SwiftDataService()
|
||||
|
||||
let container: ModelContainer
|
||||
|
||||
private init() {
|
||||
let schema = Schema([
|
||||
Category.self,
|
||||
TaskItem.self,
|
||||
TaskLog.self,
|
||||
Goal.self
|
||||
])
|
||||
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
|
||||
do {
|
||||
container = try ModelContainer(for: schema, configurations: config)
|
||||
} catch {
|
||||
fatalError("SwiftData container failed to initialize: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
/* Tab Bar */
|
||||
"tab.dashboard" = "Dashboard";
|
||||
"tab.category" = "Category";
|
||||
"tab.tasks" = "Tasks";
|
||||
"tab.goals" = "Goals";
|
||||
"tab.settings" = "Settings";
|
||||
|
||||
/* Task Tracker */
|
||||
"task.empty" = "No tasks added yet";
|
||||
"task.add" = "Add Task";
|
||||
"task.name" = "Name";
|
||||
"task.icon" = "Icon";
|
||||
"task.type" = "Type";
|
||||
"task.type.count" = "Count";
|
||||
"task.type.time" = "Time";
|
||||
"task.add.count" = "Add Count";
|
||||
"task.delete" = "Delete";
|
||||
"task.category" = "Category";
|
||||
"task.category.none" = "None";
|
||||
|
||||
/* Buttons */
|
||||
"button.cancel" = "Cancel";
|
||||
"button.add" = "Add";
|
||||
|
||||
/* Category */
|
||||
"category.empty" = "No categories yet";
|
||||
"category.add" = "Add Category";
|
||||
"category.name" = "Name";
|
||||
"category.color" = "Color";
|
||||
"category.delete" = "Delete";
|
||||
"category.task.count" = "tasks";
|
||||
|
||||
/* Goals */
|
||||
"goal.empty" = "No goals yet";
|
||||
"goal.add" = "Add Goal";
|
||||
"goal.chart.title" = "Goal Progress";
|
||||
"goal.progress" = "Progress";
|
||||
"goal.target" = "Target";
|
||||
"goal.target.task" = "Task";
|
||||
"goal.target.category" = "Category";
|
||||
"goal.no.target" = "None";
|
||||
"goal.condition" = "Condition";
|
||||
"goal.condition.count" = "Count";
|
||||
"goal.condition.time" = "Time";
|
||||
"goal.frequency" = "Frequency";
|
||||
"goal.frequency.daily" = "Daily";
|
||||
"goal.frequency.weekly" = "Weekly";
|
||||
"goal.frequency.monthly" = "Monthly";
|
||||
"goal.threshold" = "Target Value";
|
||||
"goal.delete" = "Delete";
|
||||
@ -1,50 +0,0 @@
|
||||
/* Tab Bar */
|
||||
"tab.dashboard" = "대시보드";
|
||||
"tab.category" = "카테고리";
|
||||
"tab.tasks" = "작업 추적";
|
||||
"tab.goals" = "목표";
|
||||
"tab.settings" = "설정";
|
||||
|
||||
/* Task Tracker */
|
||||
"task.empty" = "등록된 작업이 없습니다";
|
||||
"task.add" = "작업 추가";
|
||||
"task.name" = "이름";
|
||||
"task.icon" = "아이콘";
|
||||
"task.type" = "유형";
|
||||
"task.type.count" = "횟수";
|
||||
"task.type.time" = "시간";
|
||||
"task.add.count" = "횟수 추가";
|
||||
"task.delete" = "삭제";
|
||||
"task.category" = "카테고리";
|
||||
"task.category.none" = "없음";
|
||||
|
||||
/* Buttons */
|
||||
"button.cancel" = "취소";
|
||||
"button.add" = "추가";
|
||||
|
||||
/* Category */
|
||||
"category.empty" = "카테고리가 없습니다";
|
||||
"category.add" = "카테고리 추가";
|
||||
"category.name" = "이름";
|
||||
"category.color" = "색상";
|
||||
"category.delete" = "삭제";
|
||||
"category.task.count" = "작업";
|
||||
|
||||
/* Goals */
|
||||
"goal.empty" = "등록된 목표가 없습니다";
|
||||
"goal.add" = "목표 추가";
|
||||
"goal.chart.title" = "목표 진행률";
|
||||
"goal.progress" = "진행률";
|
||||
"goal.target" = "대상";
|
||||
"goal.target.task" = "작업";
|
||||
"goal.target.category" = "카테고리";
|
||||
"goal.no.target" = "없음";
|
||||
"goal.condition" = "조건";
|
||||
"goal.condition.count" = "횟수";
|
||||
"goal.condition.time" = "시간";
|
||||
"goal.frequency" = "주기";
|
||||
"goal.frequency.daily" = "매일";
|
||||
"goal.frequency.weekly" = "매주";
|
||||
"goal.frequency.monthly" = "매월";
|
||||
"goal.threshold" = "목표값";
|
||||
"goal.delete" = "삭제";
|
||||
@ -1,45 +0,0 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import SwiftData
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class CategoryViewModel {
|
||||
private(set) var categories: [Category] = []
|
||||
private var context: ModelContext?
|
||||
|
||||
static let presetColors: [String] = [
|
||||
"#2EB852", "#F5DC1F", "#4A90D9", "#F5A623",
|
||||
"#9B59B6", "#E74C3C", "#1ABC9C", "#F39C12"
|
||||
]
|
||||
|
||||
func setup(context: ModelContext) {
|
||||
self.context = context
|
||||
fetchCategories()
|
||||
}
|
||||
|
||||
func fetchCategories() {
|
||||
guard let context else { return }
|
||||
let descriptor = FetchDescriptor<Category>(sortBy: [SortDescriptor(\.name)])
|
||||
categories = (try? context.fetch(descriptor)) ?? []
|
||||
}
|
||||
|
||||
func addCategory(name: String, colorHex: String) {
|
||||
guard let context else { return }
|
||||
let category = Category(name: name, colorHex: colorHex)
|
||||
context.insert(category)
|
||||
try? context.save()
|
||||
fetchCategories()
|
||||
}
|
||||
|
||||
func deleteCategory(_ category: Category) {
|
||||
guard let context else { return }
|
||||
context.delete(category)
|
||||
try? context.save()
|
||||
fetchCategories()
|
||||
}
|
||||
|
||||
func taskCount(for category: Category) -> Int {
|
||||
category.tasks.count
|
||||
}
|
||||
}
|
||||
@ -1,93 +0,0 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import SwiftData
|
||||
|
||||
enum GoalTargetType: String, CaseIterable {
|
||||
case task
|
||||
case category
|
||||
}
|
||||
|
||||
struct GoalProgress: Identifiable {
|
||||
var id: UUID { goal.id }
|
||||
let goal: Goal
|
||||
let current: Double
|
||||
let target: Double
|
||||
|
||||
var ratio: Double { target > 0 ? min(current / target, 1.0) : 0.0 }
|
||||
var isCompleted: Bool { current >= target }
|
||||
|
||||
var displayName: String {
|
||||
if let task = goal.task { return "\(task.icon) \(task.name)" }
|
||||
if let category = goal.category { return category.name }
|
||||
return "—"
|
||||
}
|
||||
|
||||
var percentText: String { "\(Int(ratio * 100))%" }
|
||||
}
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class GoalViewModel {
|
||||
private(set) var goals: [Goal] = []
|
||||
private(set) var goalProgress: [GoalProgress] = []
|
||||
private var context: ModelContext?
|
||||
|
||||
func setup(context: ModelContext) {
|
||||
self.context = context
|
||||
fetchGoals()
|
||||
}
|
||||
|
||||
func fetchGoals() {
|
||||
guard let context else { return }
|
||||
let descriptor = FetchDescriptor<Goal>()
|
||||
goals = (try? context.fetch(descriptor)) ?? []
|
||||
computeProgress()
|
||||
}
|
||||
|
||||
func addGoal(targetType: String, conditions: Double, frequency: GoalFrequency, task: TaskItem?, category: Category?) {
|
||||
guard let context else { return }
|
||||
let goal = Goal(targetType: targetType, conditions: conditions, frequency: frequency, task: task, category: category)
|
||||
context.insert(goal)
|
||||
try? context.save()
|
||||
fetchGoals()
|
||||
}
|
||||
|
||||
func deleteGoal(_ goal: Goal) {
|
||||
guard let context else { return }
|
||||
context.delete(goal)
|
||||
try? context.save()
|
||||
fetchGoals()
|
||||
}
|
||||
|
||||
private func computeProgress() {
|
||||
let now = Date.now
|
||||
let cal = Calendar.current
|
||||
goalProgress = goals.map { goal in
|
||||
let logs = relevantLogs(for: goal, reference: now, calendar: cal)
|
||||
let current: Double = goal.targetType == "count"
|
||||
? Double(logs.reduce(0) { $0 + $1.count })
|
||||
: logs.reduce(0.0) { $0 + $1.duration }
|
||||
return GoalProgress(goal: goal, current: current, target: goal.conditions)
|
||||
}
|
||||
}
|
||||
|
||||
private func relevantLogs(for goal: Goal, reference: Date, calendar: Calendar) -> [TaskLog] {
|
||||
let allLogs: [TaskLog]
|
||||
if let task = goal.task {
|
||||
allLogs = task.logs
|
||||
} else if let category = goal.category {
|
||||
allLogs = category.tasks.flatMap { $0.logs }
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
return allLogs.filter { inPeriod($0.date, frequency: goal.frequency, reference: reference, calendar: calendar) }
|
||||
}
|
||||
|
||||
private func inPeriod(_ date: Date, frequency: GoalFrequency, reference: Date, calendar: Calendar) -> Bool {
|
||||
switch frequency {
|
||||
case .daily: return calendar.isDate(date, inSameDayAs: reference)
|
||||
case .weekly: return calendar.isDate(date, equalTo: reference, toGranularity: .weekOfYear)
|
||||
case .monthly: return calendar.isDate(date, equalTo: reference, toGranularity: .month)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,157 +0,0 @@
|
||||
import Foundation
|
||||
import Observation
|
||||
import SwiftData
|
||||
#if os(iOS)
|
||||
import ActivityKit
|
||||
#endif
|
||||
|
||||
@MainActor
|
||||
@Observable
|
||||
final class TaskViewModel {
|
||||
private(set) var tasks: [TaskItem] = []
|
||||
private(set) var activeTimerStartDates: [UUID: Date] = [:]
|
||||
private(set) var elapsedSeconds: [UUID: Int] = [:]
|
||||
|
||||
private var context: ModelContext?
|
||||
private var timerTasks: [UUID: Task<Void, Never>] = [:]
|
||||
#if os(iOS)
|
||||
private var liveActivities: [UUID: Activity<TimerActivityAttributes>] = [:]
|
||||
#endif
|
||||
|
||||
func setup(context: ModelContext) {
|
||||
self.context = context
|
||||
fetchTasks()
|
||||
}
|
||||
|
||||
func fetchTasks() {
|
||||
guard let context else { return }
|
||||
let descriptor = FetchDescriptor<TaskItem>(sortBy: [SortDescriptor(\.name)])
|
||||
tasks = (try? context.fetch(descriptor)) ?? []
|
||||
}
|
||||
|
||||
func addTask(name: String, icon: String, type: TaskType, category: Category? = nil) {
|
||||
guard let context else { return }
|
||||
let item = TaskItem(name: name, icon: icon, type: type, category: category)
|
||||
context.insert(item)
|
||||
try? context.save()
|
||||
fetchTasks()
|
||||
}
|
||||
|
||||
func deleteTask(_ task: TaskItem) {
|
||||
stopTimer(for: task)
|
||||
guard let context else { return }
|
||||
context.delete(task)
|
||||
try? context.save()
|
||||
fetchTasks()
|
||||
}
|
||||
|
||||
func addCount(to task: TaskItem) {
|
||||
guard let context else { return }
|
||||
let log = TaskLog(date: .now, duration: 0, count: 1)
|
||||
log.task = task
|
||||
context.insert(log)
|
||||
try? context.save()
|
||||
}
|
||||
|
||||
func toggleTimer(for task: TaskItem) {
|
||||
if activeTimerStartDates[task.id] != nil {
|
||||
stopTimer(for: task)
|
||||
} else {
|
||||
startTimer(for: task)
|
||||
}
|
||||
}
|
||||
|
||||
func isTimerRunning(for taskID: UUID) -> Bool {
|
||||
activeTimerStartDates[taskID] != nil
|
||||
}
|
||||
|
||||
func formattedElapsed(for taskID: UUID) -> String {
|
||||
let s = elapsedSeconds[taskID] ?? 0
|
||||
let h = s / 3600
|
||||
let m = (s % 3600) / 60
|
||||
let sec = s % 60
|
||||
return h > 0
|
||||
? String(format: "%d:%02d:%02d", h, m, sec)
|
||||
: String(format: "%02d:%02d", m, sec)
|
||||
}
|
||||
|
||||
func todayCount(for task: TaskItem) -> Int {
|
||||
task.logs
|
||||
.filter { Calendar.current.isDateInToday($0.date) }
|
||||
.reduce(0) { $0 + $1.count }
|
||||
}
|
||||
|
||||
// MARK: - Timer
|
||||
|
||||
private func startTimer(for task: TaskItem) {
|
||||
let startDate = Date.now
|
||||
activeTimerStartDates[task.id] = startDate
|
||||
elapsedSeconds[task.id] = 0
|
||||
startLiveActivity(for: task, startDate: startDate)
|
||||
|
||||
let taskID = task.id
|
||||
timerTasks[taskID] = Task { [weak self] in
|
||||
while !Task.isCancelled {
|
||||
try? await Task.sleep(for: .seconds(1))
|
||||
guard !Task.isCancelled, let self else { break }
|
||||
let elapsed = Int(Date.now.timeIntervalSince(startDate))
|
||||
self.elapsedSeconds[taskID] = elapsed
|
||||
await self.updateLiveActivity(taskID: taskID, elapsed: elapsed, startDate: startDate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func stopTimer(for task: TaskItem) {
|
||||
guard let startDate = activeTimerStartDates[task.id] else { return }
|
||||
|
||||
timerTasks[task.id]?.cancel()
|
||||
timerTasks.removeValue(forKey: task.id)
|
||||
|
||||
let duration = Date.now.timeIntervalSince(startDate)
|
||||
activeTimerStartDates.removeValue(forKey: task.id)
|
||||
elapsedSeconds.removeValue(forKey: task.id)
|
||||
|
||||
endLiveActivity(for: task)
|
||||
|
||||
guard let context else { return }
|
||||
let log = TaskLog(date: .now, duration: duration, count: 0)
|
||||
log.task = task
|
||||
context.insert(log)
|
||||
try? context.save()
|
||||
}
|
||||
|
||||
// MARK: - ActivityKit
|
||||
|
||||
private func startLiveActivity(for task: TaskItem, startDate: Date) {
|
||||
#if os(iOS)
|
||||
guard ActivityAuthorizationInfo().areActivitiesEnabled else { return }
|
||||
let attrs = TimerActivityAttributes(taskName: task.name, taskIcon: task.icon)
|
||||
let state = TimerActivityAttributes.ContentState(startDate: startDate, elapsedSeconds: 0)
|
||||
let content = ActivityContent(state: state, staleDate: nil)
|
||||
do {
|
||||
let activity = try Activity.request(attributes: attrs, content: content)
|
||||
liveActivities[task.id] = activity
|
||||
} catch {
|
||||
// Live Activity unavailable (simulator, denied, or OS < 16.2)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private func updateLiveActivity(taskID: UUID, elapsed: Int, startDate: Date) async {
|
||||
#if os(iOS)
|
||||
guard let activity = liveActivities[taskID] else { return }
|
||||
let state = TimerActivityAttributes.ContentState(startDate: startDate, elapsedSeconds: elapsed)
|
||||
await activity.update(ActivityContent(state: state, staleDate: nil))
|
||||
#endif
|
||||
}
|
||||
|
||||
private func endLiveActivity(for task: TaskItem) {
|
||||
#if os(iOS)
|
||||
guard let activity = liveActivities[task.id] else { return }
|
||||
liveActivities.removeValue(forKey: task.id)
|
||||
Task {
|
||||
await activity.end(nil, dismissalPolicy: .immediate)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
struct CategoryView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@State private var viewModel = CategoryViewModel()
|
||||
@State private var showAddSheet = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
Group {
|
||||
if viewModel.categories.isEmpty {
|
||||
emptyState
|
||||
} else {
|
||||
categoryList
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "tab.category"))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button { showAddSheet = true } label: {
|
||||
Image(systemName: "plus")
|
||||
.foregroundStyle(Theme.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddSheet) {
|
||||
AddCategorySheet { name, colorHex in
|
||||
viewModel.addCategory(name: name, colorHex: colorHex)
|
||||
}
|
||||
}
|
||||
.onAppear { viewModel.setup(context: modelContext) }
|
||||
}
|
||||
|
||||
private var emptyState: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "folder.badge.plus")
|
||||
.font(.system(size: 56))
|
||||
.foregroundStyle(Theme.green.opacity(0.6))
|
||||
Text(String(localized: "category.empty"))
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.6))
|
||||
}
|
||||
}
|
||||
|
||||
private var categoryList: some View {
|
||||
List {
|
||||
ForEach(viewModel.categories) { category in
|
||||
CategoryRow(category: category, taskCount: viewModel.taskCount(for: category))
|
||||
.listRowBackground(Theme.surface)
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button(role: .destructive) {
|
||||
viewModel.deleteCategory(category)
|
||||
} label: {
|
||||
Label(String(localized: "category.delete"), systemImage: "trash")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Category Row
|
||||
|
||||
private struct CategoryRow: View {
|
||||
let category: Category
|
||||
let taskCount: Int
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
Circle()
|
||||
.fill(Color(hex: category.colorHex))
|
||||
.frame(width: 14, height: 14)
|
||||
|
||||
Text(category.name)
|
||||
.font(.body)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text("\(taskCount) \(String(localized: "category.task.count"))")
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.5))
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 4)
|
||||
.background(Theme.green.opacity(0.12), in: Capsule())
|
||||
}
|
||||
.padding(.vertical, 6)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Add Category Sheet
|
||||
|
||||
private struct AddCategorySheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@State private var name = ""
|
||||
@State private var selectedColor = CategoryViewModel.presetColors[0]
|
||||
|
||||
let onAdd: (String, String) -> Void
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
TextField(String(localized: "category.name"), text: $name)
|
||||
}
|
||||
|
||||
Section(String(localized: "category.color")) {
|
||||
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 4), spacing: 16) {
|
||||
ForEach(CategoryViewModel.presetColors, id: \.self) { colorHex in
|
||||
Circle()
|
||||
.fill(Color(hex: colorHex))
|
||||
.frame(width: 42, height: 42)
|
||||
.overlay(alignment: .center) {
|
||||
if selectedColor == colorHex {
|
||||
Image(systemName: "checkmark")
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(.white)
|
||||
}
|
||||
}
|
||||
.overlay(
|
||||
Circle()
|
||||
.strokeBorder(
|
||||
selectedColor == colorHex ? Theme.primaryText.opacity(0.4) : Color.clear,
|
||||
lineWidth: 2
|
||||
)
|
||||
)
|
||||
.onTapGesture { selectedColor = colorHex }
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 8)
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "category.add"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(String(localized: "button.cancel")) { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(String(localized: "button.add")) {
|
||||
let trimmed = name.trimmingCharacters(in: .whitespaces)
|
||||
guard !trimmed.isEmpty else { return }
|
||||
onAdd(trimmed, selectedColor)
|
||||
dismiss()
|
||||
}
|
||||
.disabled(name.trimmingCharacters(in: .whitespaces).isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Color Hex Extension
|
||||
|
||||
extension Color {
|
||||
init(hex: String) {
|
||||
let hex = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
|
||||
var int: UInt64 = 0
|
||||
Scanner(string: hex).scanHexInt64(&int)
|
||||
let r = Double((int >> 16) & 0xFF) / 255
|
||||
let g = Double((int >> 8) & 0xFF) / 255
|
||||
let b = Double(int & 0xFF) / 255
|
||||
self.init(red: r, green: g, blue: b)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
CategoryView()
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct DashboardView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
Text(String(localized: "tab.dashboard"))
|
||||
.font(.title)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
}
|
||||
.navigationTitle(String(localized: "tab.dashboard"))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
DashboardView()
|
||||
}
|
||||
@ -1,306 +0,0 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
import Charts
|
||||
|
||||
struct GoalView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@State private var viewModel = GoalViewModel()
|
||||
@State private var showAddSheet = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
Group {
|
||||
if viewModel.goals.isEmpty {
|
||||
emptyState
|
||||
} else {
|
||||
goalContent
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "tab.goals"))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button { showAddSheet = true } label: {
|
||||
Image(systemName: "plus")
|
||||
.foregroundStyle(Theme.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddSheet) {
|
||||
AddGoalSheet { targetType, conditions, frequency, task, category in
|
||||
viewModel.addGoal(targetType: targetType, conditions: conditions, frequency: frequency, task: task, category: category)
|
||||
}
|
||||
}
|
||||
.onAppear { viewModel.setup(context: modelContext) }
|
||||
}
|
||||
|
||||
private var emptyState: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "flag.badge.ellipsis")
|
||||
.font(.system(size: 56))
|
||||
.foregroundStyle(Theme.green.opacity(0.6))
|
||||
Text(String(localized: "goal.empty"))
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.6))
|
||||
}
|
||||
}
|
||||
|
||||
private var goalContent: some View {
|
||||
ScrollView {
|
||||
VStack(spacing: 16) {
|
||||
progressChart
|
||||
.padding(.horizontal)
|
||||
goalCards
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.padding(.vertical)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Chart
|
||||
|
||||
private var progressChart: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text(String(localized: "goal.chart.title"))
|
||||
.font(.headline)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
|
||||
Chart(viewModel.goalProgress) { progress in
|
||||
BarMark(
|
||||
x: .value(String(localized: "goal.progress"), progress.ratio * 100),
|
||||
y: .value("", progress.displayName)
|
||||
)
|
||||
.foregroundStyle(progress.isCompleted ? Theme.green : Theme.yellow)
|
||||
.cornerRadius(4)
|
||||
}
|
||||
.chartXScale(domain: 0.0...100.0)
|
||||
.chartXAxis {
|
||||
AxisMarks(values: [0.0, 25.0, 50.0, 75.0, 100.0]) { value in
|
||||
AxisGridLine()
|
||||
AxisValueLabel {
|
||||
if let v = value.as(Double.self) {
|
||||
Text("\(Int(v))%")
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.5))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chartYAxis {
|
||||
AxisMarks { _ in
|
||||
AxisValueLabel()
|
||||
}
|
||||
}
|
||||
.frame(height: CGFloat(viewModel.goalProgress.count) * 48 + 20)
|
||||
}
|
||||
.padding()
|
||||
.background(Theme.surface, in: RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
|
||||
// MARK: - Goal Cards
|
||||
|
||||
private var goalCards: some View {
|
||||
VStack(spacing: 12) {
|
||||
ForEach(viewModel.goalProgress) { progress in
|
||||
GoalCard(progress: progress) {
|
||||
viewModel.deleteGoal(progress.goal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Goal Card
|
||||
|
||||
private struct GoalCard: View {
|
||||
let progress: GoalProgress
|
||||
let onDelete: () -> Void
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack(alignment: .top) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(progress.displayName)
|
||||
.font(.body)
|
||||
.fontWeight(.semibold)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
Text(subtitleText)
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.5))
|
||||
}
|
||||
Spacer()
|
||||
Button(role: .destructive, action: onDelete) {
|
||||
Image(systemName: "trash")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.red.opacity(0.7))
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
ProgressView(value: progress.ratio)
|
||||
.tint(progress.isCompleted ? Theme.green : Theme.yellow)
|
||||
|
||||
HStack {
|
||||
Text(currentText)
|
||||
.font(.caption)
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.6))
|
||||
Spacer()
|
||||
Text(progress.percentText)
|
||||
.font(.caption)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(progress.isCompleted ? Theme.green : Theme.yellow)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(Theme.surface, in: RoundedRectangle(cornerRadius: 12))
|
||||
}
|
||||
|
||||
private var subtitleText: String {
|
||||
let cond = progress.goal.targetType == "count"
|
||||
? String(localized: "goal.condition.count")
|
||||
: String(localized: "goal.condition.time")
|
||||
let freq: String
|
||||
switch progress.goal.frequency {
|
||||
case .daily: freq = String(localized: "goal.frequency.daily")
|
||||
case .weekly: freq = String(localized: "goal.frequency.weekly")
|
||||
case .monthly: freq = String(localized: "goal.frequency.monthly")
|
||||
}
|
||||
return "\(cond) · \(freq)"
|
||||
}
|
||||
|
||||
private var currentText: String {
|
||||
if progress.goal.targetType == "count" {
|
||||
return "\(Int(progress.current)) / \(Int(progress.target)) \(String(localized: "goal.condition.count"))"
|
||||
} else {
|
||||
let currentMins = Int(progress.current) / 60
|
||||
let targetMins = Int(progress.target) / 60
|
||||
return "\(currentMins)m / \(targetMins)m"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Add Goal Sheet
|
||||
|
||||
private struct AddGoalSheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Query(sort: \TaskItem.name) private var tasks: [TaskItem]
|
||||
@Query(sort: \Category.name) private var categories: [Category]
|
||||
|
||||
@State private var targetType: GoalTargetType = .task
|
||||
@State private var selectedTask: TaskItem?
|
||||
@State private var selectedCategory: Category?
|
||||
@State private var conditionType: String = "count"
|
||||
@State private var threshold: Double = 5
|
||||
@State private var frequency: GoalFrequency = .daily
|
||||
|
||||
let onAdd: (String, Double, GoalFrequency, TaskItem?, Category?) -> Void
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section(header: Text(String(localized: "goal.target"))) {
|
||||
Picker(String(localized: "goal.target"), selection: $targetType) {
|
||||
Text(String(localized: "goal.target.task")).tag(GoalTargetType.task)
|
||||
Text(String(localized: "goal.target.category")).tag(GoalTargetType.category)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.onChange(of: targetType) { _, _ in
|
||||
selectedTask = nil
|
||||
selectedCategory = nil
|
||||
}
|
||||
|
||||
if targetType == .task {
|
||||
Picker(String(localized: "goal.target.task"), selection: $selectedTask) {
|
||||
Text(String(localized: "goal.no.target")).tag(Optional<TaskItem>.none)
|
||||
ForEach(tasks) { task in
|
||||
Text("\(task.icon) \(task.name)").tag(Optional(task))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Picker(String(localized: "goal.target.category"), selection: $selectedCategory) {
|
||||
Text(String(localized: "goal.no.target")).tag(Optional<Category>.none)
|
||||
ForEach(categories) { cat in
|
||||
Text(cat.name).tag(Optional(cat))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text(String(localized: "goal.condition"))) {
|
||||
Picker(String(localized: "goal.condition"), selection: $conditionType) {
|
||||
Text(String(localized: "goal.condition.count")).tag("count")
|
||||
Text(String(localized: "goal.condition.time")).tag("duration")
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.onChange(of: conditionType) { _, _ in threshold = 5 }
|
||||
}
|
||||
|
||||
Section(header: Text(String(localized: "goal.threshold"))) {
|
||||
HStack {
|
||||
Slider(
|
||||
value: $threshold,
|
||||
in: conditionType == "count" ? 1.0...100.0 : 5.0...240.0,
|
||||
step: conditionType == "count" ? 1.0 : 5.0
|
||||
)
|
||||
.tint(Theme.green)
|
||||
Text(thresholdText)
|
||||
.font(.body)
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(Theme.green)
|
||||
.frame(width: 52, alignment: .trailing)
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text(String(localized: "goal.frequency"))) {
|
||||
Picker(String(localized: "goal.frequency"), selection: $frequency) {
|
||||
Text(String(localized: "goal.frequency.daily")).tag(GoalFrequency.daily)
|
||||
Text(String(localized: "goal.frequency.weekly")).tag(GoalFrequency.weekly)
|
||||
Text(String(localized: "goal.frequency.monthly")).tag(GoalFrequency.monthly)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "goal.add"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(String(localized: "button.cancel")) { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(String(localized: "button.add")) {
|
||||
let storedConditions = conditionType == "duration" ? threshold * 60.0 : threshold
|
||||
onAdd(
|
||||
conditionType,
|
||||
storedConditions,
|
||||
frequency,
|
||||
targetType == .task ? selectedTask : nil,
|
||||
targetType == .category ? selectedCategory : nil
|
||||
)
|
||||
dismiss()
|
||||
}
|
||||
.disabled(!canAdd)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var thresholdText: String {
|
||||
if conditionType == "count" {
|
||||
return "\(Int(threshold))"
|
||||
} else {
|
||||
let h = Int(threshold) / 60
|
||||
let m = Int(threshold) % 60
|
||||
return h > 0 ? "\(h)h\(m)m" : "\(Int(threshold))m"
|
||||
}
|
||||
}
|
||||
|
||||
private var canAdd: Bool {
|
||||
targetType == .task ? selectedTask != nil : selectedCategory != nil
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NavigationStack {
|
||||
GoalView()
|
||||
}
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct LaunchScreenView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background
|
||||
.ignoresSafeArea()
|
||||
VStack(spacing: 12) {
|
||||
Text("🍋🍈")
|
||||
.font(.system(size: 60))
|
||||
Text("LemonLimeTracker")
|
||||
.font(.largeTitle)
|
||||
.fontWeight(.bold)
|
||||
.foregroundStyle(Theme.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
LaunchScreenView()
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct MainTabView: View {
|
||||
var body: some View {
|
||||
TabView {
|
||||
NavigationStack {
|
||||
DashboardView()
|
||||
}
|
||||
.tabItem {
|
||||
Label(String(localized: "tab.dashboard"), systemImage: "chart.bar.fill")
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
CategoryView()
|
||||
}
|
||||
.tabItem {
|
||||
Label(String(localized: "tab.category"), systemImage: "folder.fill")
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
TaskTrackerView()
|
||||
}
|
||||
.tabItem {
|
||||
Label(String(localized: "tab.tasks"), systemImage: "checkmark.circle.fill")
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
GoalView()
|
||||
}
|
||||
.tabItem {
|
||||
Label(String(localized: "tab.goals"), systemImage: "flag.fill")
|
||||
}
|
||||
|
||||
NavigationStack {
|
||||
SettingsView()
|
||||
}
|
||||
.tabItem {
|
||||
Label(String(localized: "tab.settings"), systemImage: "gearshape.fill")
|
||||
}
|
||||
}
|
||||
.tint(Theme.green)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
MainTabView()
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct RootView: View {
|
||||
@State private var isLaunching = true
|
||||
|
||||
var body: some View {
|
||||
if isLaunching {
|
||||
LaunchScreenView()
|
||||
.onAppear {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
withAnimation(.easeInOut(duration: 0.4)) {
|
||||
isLaunching = false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
MainTabView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
RootView()
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
import SwiftUI
|
||||
|
||||
struct SettingsView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
Text(String(localized: "tab.settings"))
|
||||
.font(.title)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
}
|
||||
.navigationTitle(String(localized: "tab.settings"))
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
SettingsView()
|
||||
}
|
||||
@ -1,212 +0,0 @@
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
|
||||
struct TaskTrackerView: View {
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
@State private var viewModel = TaskViewModel()
|
||||
@State private var showAddSheet = false
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
Theme.background.ignoresSafeArea()
|
||||
Group {
|
||||
if viewModel.tasks.isEmpty {
|
||||
emptyState
|
||||
} else {
|
||||
taskList
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "tab.tasks"))
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button {
|
||||
showAddSheet = true
|
||||
} label: {
|
||||
Image(systemName: "plus")
|
||||
.foregroundStyle(Theme.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showAddSheet) {
|
||||
AddTaskSheet { name, icon, type, category in
|
||||
viewModel.addTask(name: name, icon: icon, type: type, category: category)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
viewModel.setup(context: modelContext)
|
||||
}
|
||||
}
|
||||
|
||||
private var emptyState: some View {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "checkmark.circle.badge.plus")
|
||||
.font(.system(size: 56))
|
||||
.foregroundStyle(Theme.green.opacity(0.6))
|
||||
Text(String(localized: "task.empty"))
|
||||
.foregroundStyle(Theme.primaryText.opacity(0.6))
|
||||
}
|
||||
}
|
||||
|
||||
private var taskList: some View {
|
||||
List {
|
||||
ForEach(viewModel.tasks) { task in
|
||||
TaskRow(task: task, viewModel: viewModel)
|
||||
.listRowBackground(Theme.surface)
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: false) {
|
||||
Button(role: .destructive) {
|
||||
viewModel.deleteTask(task)
|
||||
} label: {
|
||||
Label(String(localized: "task.delete"), systemImage: "trash")
|
||||
}
|
||||
}
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
if task.type == .count {
|
||||
Button {
|
||||
viewModel.addCount(to: task)
|
||||
} label: {
|
||||
Label(String(localized: "task.add.count"), systemImage: "plus")
|
||||
}
|
||||
.tint(Theme.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.listStyle(.plain)
|
||||
.scrollContentBackground(.hidden)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Task Row
|
||||
|
||||
private struct TaskRow: View {
|
||||
let task: TaskItem
|
||||
let viewModel: TaskViewModel
|
||||
|
||||
private var isRunning: Bool { viewModel.isTimerRunning(for: task.id) }
|
||||
|
||||
var body: some View {
|
||||
HStack(spacing: 12) {
|
||||
Text(task.icon)
|
||||
.font(.title2)
|
||||
.frame(width: 36)
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(task.name)
|
||||
.font(.body)
|
||||
.fontWeight(.medium)
|
||||
.foregroundStyle(Theme.primaryText)
|
||||
if let category = task.category {
|
||||
Text(category.name)
|
||||
.font(.caption2)
|
||||
.foregroundStyle(Theme.green)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
if task.type == .time {
|
||||
timeControls
|
||||
} else {
|
||||
countBadge
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
|
||||
private var timeControls: some View {
|
||||
HStack(spacing: 8) {
|
||||
if isRunning {
|
||||
Text(viewModel.formattedElapsed(for: task.id))
|
||||
.font(.system(.subheadline, design: .monospaced))
|
||||
.foregroundStyle(Theme.green)
|
||||
.contentTransition(.numericText())
|
||||
.animation(.linear(duration: 0.3), value: viewModel.elapsedSeconds[task.id])
|
||||
}
|
||||
Button {
|
||||
viewModel.toggleTimer(for: task)
|
||||
} label: {
|
||||
Image(systemName: isRunning ? "stop.fill" : "play.fill")
|
||||
.font(.title3)
|
||||
.foregroundStyle(isRunning ? Theme.yellow : Theme.green)
|
||||
.frame(width: 36, height: 36)
|
||||
.background(
|
||||
Circle()
|
||||
.fill(isRunning ? Theme.yellow.opacity(0.15) : Theme.green.opacity(0.15))
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
private var countBadge: some View {
|
||||
let count = viewModel.todayCount(for: task)
|
||||
return Text("×\(count)")
|
||||
.font(.headline)
|
||||
.monospacedDigit()
|
||||
.foregroundStyle(count > 0 ? Theme.green : Theme.primaryText.opacity(0.3))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Add Task Sheet
|
||||
|
||||
private struct AddTaskSheet: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@Query private var categories: [Category]
|
||||
|
||||
@State private var name = ""
|
||||
@State private var icon = "⭐"
|
||||
@State private var type: TaskType = .count
|
||||
@State private var selectedCategory: Category?
|
||||
|
||||
let onAdd: (String, String, TaskType, Category?) -> Void
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
Section {
|
||||
TextField(String(localized: "task.name"), text: $name)
|
||||
TextField(String(localized: "task.icon"), text: $icon)
|
||||
}
|
||||
Section {
|
||||
Picker(String(localized: "task.type"), selection: $type) {
|
||||
Text(String(localized: "task.type.count")).tag(TaskType.count)
|
||||
Text(String(localized: "task.type.time")).tag(TaskType.time)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
}
|
||||
if !categories.isEmpty {
|
||||
Section {
|
||||
Picker(String(localized: "task.category"), selection: $selectedCategory) {
|
||||
Text(String(localized: "task.category.none")).tag(Optional<Category>.none)
|
||||
ForEach(categories) { cat in
|
||||
Text(cat.name).tag(Optional(cat))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle(String(localized: "task.add"))
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button(String(localized: "button.cancel")) { dismiss() }
|
||||
}
|
||||
ToolbarItem(placement: .confirmationAction) {
|
||||
Button(String(localized: "button.add")) {
|
||||
let trimmed = name.trimmingCharacters(in: .whitespaces)
|
||||
guard !trimmed.isEmpty else { return }
|
||||
let resolvedIcon = icon.trimmingCharacters(in: .whitespaces).isEmpty ? "⭐" : icon
|
||||
onAdd(trimmed, resolvedIcon, type, selectedCategory)
|
||||
dismiss()
|
||||
}
|
||||
.disabled(name.trimmingCharacters(in: .whitespaces).isEmpty)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TaskTrackerView()
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
<?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>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>ko</string>
|
||||
<string>en</string>
|
||||
</array>
|
||||
<key>NSSupportsLiveActivities</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,543 +0,0 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 77;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
7280EB402FE41F11006B83D9 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7280EB3F2FE41F10006B83D9 /* WidgetKit.framework */; };
|
||||
7280EB422FE41F11006B83D9 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7280EB412FE41F11006B83D9 /* SwiftUI.framework */; };
|
||||
7280EB4F2FE41F12006B83D9 /* LemonLimeWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 7280EB3D2FE41F10006B83D9 /* LemonLimeWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
7280EB4D2FE41F12006B83D9 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 7280EAF02FE40D38006B83D9 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 7280EB3C2FE41F10006B83D9;
|
||||
remoteInfo = LemonLimeWidgetExtension;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
7280EB542FE41F12006B83D9 /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
7280EB4F2FE41F12006B83D9 /* LemonLimeWidgetExtension.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
7280EAF82FE40D38006B83D9 /* LemonLimeTracker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LemonLimeTracker.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7280EB1E2FE41A14006B83D9 /* LemonLimeTracker-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "LemonLimeTracker-Info.plist"; sourceTree = "<group>"; };
|
||||
7280EB3D2FE41F10006B83D9 /* LemonLimeWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = LemonLimeWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
7280EB3F2FE41F10006B83D9 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
|
||||
7280EB412FE41F11006B83D9 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
7280EB502FE41F12006B83D9 /* Exceptions for "LemonLimeWidget" folder in "LemonLimeWidgetExtension" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 7280EB3C2FE41F10006B83D9 /* LemonLimeWidgetExtension */;
|
||||
};
|
||||
7280EB5A2FE42219006B83D9 /* Exceptions for "IOS" folder in "LemonLimeWidgetExtension" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Core/TimerActivityAttributes.swift,
|
||||
);
|
||||
target = 7280EB3C2FE41F10006B83D9 /* LemonLimeWidgetExtension */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
7280EAFA2FE40D38006B83D9 /* IOS */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
7280EB5A2FE42219006B83D9 /* Exceptions for "IOS" folder in "LemonLimeWidgetExtension" target */,
|
||||
);
|
||||
path = IOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7280EB432FE41F11006B83D9 /* LemonLimeWidget */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
7280EB502FE41F12006B83D9 /* Exceptions for "LemonLimeWidget" folder in "LemonLimeWidgetExtension" target */,
|
||||
);
|
||||
path = LemonLimeWidget;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
7280EAF52FE40D38006B83D9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7280EB3A2FE41F10006B83D9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7280EB422FE41F11006B83D9 /* SwiftUI.framework in Frameworks */,
|
||||
7280EB402FE41F11006B83D9 /* WidgetKit.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
7280EAEF2FE40D38006B83D9 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7280EB1E2FE41A14006B83D9 /* LemonLimeTracker-Info.plist */,
|
||||
7280EAFA2FE40D38006B83D9 /* IOS */,
|
||||
7280EB432FE41F11006B83D9 /* LemonLimeWidget */,
|
||||
7280EB3E2FE41F10006B83D9 /* Frameworks */,
|
||||
7280EAF92FE40D38006B83D9 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7280EAF92FE40D38006B83D9 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7280EAF82FE40D38006B83D9 /* LemonLimeTracker.app */,
|
||||
7280EB3D2FE41F10006B83D9 /* LemonLimeWidgetExtension.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
7280EB3E2FE41F10006B83D9 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
7280EB3F2FE41F10006B83D9 /* WidgetKit.framework */,
|
||||
7280EB412FE41F11006B83D9 /* SwiftUI.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
7280EAF72FE40D38006B83D9 /* LemonLimeTracker */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7280EB032FE40D39006B83D9 /* Build configuration list for PBXNativeTarget "LemonLimeTracker" */;
|
||||
buildPhases = (
|
||||
7280EAF42FE40D38006B83D9 /* Sources */,
|
||||
7280EAF52FE40D38006B83D9 /* Frameworks */,
|
||||
7280EAF62FE40D38006B83D9 /* Resources */,
|
||||
7280EB542FE41F12006B83D9 /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
7280EB4E2FE41F12006B83D9 /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
7280EAFA2FE40D38006B83D9 /* IOS */,
|
||||
);
|
||||
name = LemonLimeTracker;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = LemonLimeTracker;
|
||||
productReference = 7280EAF82FE40D38006B83D9 /* LemonLimeTracker.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
7280EB3C2FE41F10006B83D9 /* LemonLimeWidgetExtension */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 7280EB512FE41F12006B83D9 /* Build configuration list for PBXNativeTarget "LemonLimeWidgetExtension" */;
|
||||
buildPhases = (
|
||||
7280EB392FE41F10006B83D9 /* Sources */,
|
||||
7280EB3A2FE41F10006B83D9 /* Frameworks */,
|
||||
7280EB3B2FE41F10006B83D9 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
7280EB432FE41F11006B83D9 /* LemonLimeWidget */,
|
||||
);
|
||||
name = LemonLimeWidgetExtension;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = LemonLimeWidgetExtension;
|
||||
productReference = 7280EB3D2FE41F10006B83D9 /* LemonLimeWidgetExtension.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
7280EAF02FE40D38006B83D9 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 2650;
|
||||
LastUpgradeCheck = 2650;
|
||||
TargetAttributes = {
|
||||
7280EAF72FE40D38006B83D9 = {
|
||||
CreatedOnToolsVersion = 26.5;
|
||||
};
|
||||
7280EB3C2FE41F10006B83D9 = {
|
||||
CreatedOnToolsVersion = 26.5;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 7280EAF32FE40D38006B83D9 /* Build configuration list for PBXProject "LemonLimeTracker" */;
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
ko,
|
||||
);
|
||||
mainGroup = 7280EAEF2FE40D38006B83D9;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = 7280EAF92FE40D38006B83D9 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
7280EAF72FE40D38006B83D9 /* LemonLimeTracker */,
|
||||
7280EB3C2FE41F10006B83D9 /* LemonLimeWidgetExtension */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
7280EAF62FE40D38006B83D9 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7280EB3B2FE41F10006B83D9 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
7280EAF42FE40D38006B83D9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
7280EB392FE41F10006B83D9 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
7280EB4E2FE41F12006B83D9 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 7280EB3C2FE41F10006B83D9 /* LemonLimeWidgetExtension */;
|
||||
targetProxy = 7280EB4D2FE41F12006B83D9 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
7280EB012FE40D39006B83D9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7280EB022FE40D39006B83D9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 26.5;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7280EB042FE40D39006B83D9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "LemonLimeTracker-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.LemonLimeTracker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7280EB052FE40D39006B83D9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "LemonLimeTracker-Info.plist";
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "";
|
||||
INFOPLIST_KEY_LSApplicationCategoryType = "";
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.LemonLimeTracker;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
7280EB522FE41F12006B83D9 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = LemonLimeWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LemonLimeWidget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.LemonLimeTracker.LemonLimeWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
7280EB532FE41F12006B83D9 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = LemonLimeWidget/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = LemonLimeWidget;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.yechan.LemonLimeTracker.LemonLimeWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||
SWIFT_APPROACHABLE_CONCURRENCY = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
7280EAF32FE40D38006B83D9 /* Build configuration list for PBXProject "LemonLimeTracker" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7280EB012FE40D39006B83D9 /* Debug */,
|
||||
7280EB022FE40D39006B83D9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7280EB032FE40D39006B83D9 /* Build configuration list for PBXNativeTarget "LemonLimeTracker" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7280EB042FE40D39006B83D9 /* Debug */,
|
||||
7280EB052FE40D39006B83D9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
7280EB512FE41F12006B83D9 /* Build configuration list for PBXNativeTarget "LemonLimeWidgetExtension" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
7280EB522FE41F12006B83D9 /* Debug */,
|
||||
7280EB532FE41F12006B83D9 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 7280EAF02FE40D38006B83D9 /* Project object */;
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@ -1,19 +0,0 @@
|
||||
<?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>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>LemonLimeTracker.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>LemonLimeWidgetExtension.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
<?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>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.widgetkit-extension</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -1,84 +0,0 @@
|
||||
//
|
||||
// LemonLimeWidget.swift
|
||||
// LemonLimeWidget
|
||||
//
|
||||
// Created by 송예찬 on 6/18/26.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
struct Provider: TimelineProvider {
|
||||
func placeholder(in context: Context) -> SimpleEntry {
|
||||
SimpleEntry(date: Date(), emoji: "😀")
|
||||
}
|
||||
|
||||
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
|
||||
let entry = SimpleEntry(date: Date(), emoji: "😀")
|
||||
completion(entry)
|
||||
}
|
||||
|
||||
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
|
||||
var entries: [SimpleEntry] = []
|
||||
|
||||
// Generate a timeline consisting of five entries an hour apart, starting from the current date.
|
||||
let currentDate = Date()
|
||||
for hourOffset in 0 ..< 5 {
|
||||
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
|
||||
let entry = SimpleEntry(date: entryDate, emoji: "😀")
|
||||
entries.append(entry)
|
||||
}
|
||||
|
||||
let timeline = Timeline(entries: entries, policy: .atEnd)
|
||||
completion(timeline)
|
||||
}
|
||||
|
||||
// func relevances() async -> WidgetRelevances<Void> {
|
||||
// // Generate a list containing the contexts this widget is relevant in.
|
||||
// }
|
||||
}
|
||||
|
||||
struct SimpleEntry: TimelineEntry {
|
||||
let date: Date
|
||||
let emoji: String
|
||||
}
|
||||
|
||||
struct LemonLimeWidgetEntryView : View {
|
||||
var entry: Provider.Entry
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text("Time:")
|
||||
Text(entry.date, style: .time)
|
||||
|
||||
Text("Emoji:")
|
||||
Text(entry.emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LemonLimeWidget: Widget {
|
||||
let kind: String = "LemonLimeWidget"
|
||||
|
||||
var body: some WidgetConfiguration {
|
||||
StaticConfiguration(kind: kind, provider: Provider()) { entry in
|
||||
if #available(iOS 17.0, *) {
|
||||
LemonLimeWidgetEntryView(entry: entry)
|
||||
.containerBackground(.fill.tertiary, for: .widget)
|
||||
} else {
|
||||
LemonLimeWidgetEntryView(entry: entry)
|
||||
.padding()
|
||||
.background()
|
||||
}
|
||||
}
|
||||
.configurationDisplayName("My Widget")
|
||||
.description("This is an example widget.")
|
||||
}
|
||||
}
|
||||
|
||||
#Preview(as: .systemSmall) {
|
||||
LemonLimeWidget()
|
||||
} timeline: {
|
||||
SimpleEntry(date: .now, emoji: "😀")
|
||||
SimpleEntry(date: .now, emoji: "🤩")
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
//
|
||||
// LemonLimeWidgetBundle.swift
|
||||
// LemonLimeWidget
|
||||
//
|
||||
// Created by 송예찬 on 6/18/26.
|
||||
//
|
||||
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct LemonLimeWidgetBundle: WidgetBundle {
|
||||
var body: some Widget {
|
||||
LemonLimeWidget()
|
||||
LemonLimeWidgetLiveActivity()
|
||||
}
|
||||
}
|
||||
@ -1,125 +0,0 @@
|
||||
//
|
||||
// LemonLimeWidgetLiveActivity.swift
|
||||
// LemonLimeWidget
|
||||
//
|
||||
// Created by 송예찬 on 6/18/26.
|
||||
//
|
||||
|
||||
import ActivityKit
|
||||
import WidgetKit
|
||||
import SwiftUI
|
||||
|
||||
private let timerEndDate = Date.distantFuture
|
||||
|
||||
struct LemonLimeWidgetLiveActivity: Widget {
|
||||
var body: some WidgetConfiguration {
|
||||
ActivityConfiguration(for: TimerActivityAttributes.self) { context in
|
||||
// MARK: Lock Screen / Banner
|
||||
HStack(spacing: 14) {
|
||||
Image(systemName: "timer")
|
||||
.font(.title2.weight(.semibold))
|
||||
.foregroundStyle(.yellow)
|
||||
|
||||
VStack(alignment: .leading, spacing: 3) {
|
||||
Text(context.attributes.taskName)
|
||||
.font(.subheadline.weight(.semibold))
|
||||
.foregroundStyle(.white)
|
||||
.lineLimit(1)
|
||||
|
||||
Text(timerInterval: context.state.startDate...timerEndDate, countsDown: false)
|
||||
.font(.caption.monospacedDigit())
|
||||
.foregroundStyle(.yellow)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(context.attributes.taskIcon)
|
||||
.font(.largeTitle)
|
||||
}
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.vertical, 12)
|
||||
.activityBackgroundTint(Color(red: 0.08, green: 0.30, blue: 0.08))
|
||||
.activitySystemActionForegroundColor(.yellow)
|
||||
|
||||
} dynamicIsland: { context in
|
||||
DynamicIsland {
|
||||
// MARK: Expanded – top row
|
||||
DynamicIslandExpandedRegion(.leading) {
|
||||
Label {
|
||||
Text(context.attributes.taskName)
|
||||
.font(.caption.weight(.semibold))
|
||||
.lineLimit(1)
|
||||
} icon: {
|
||||
Text(context.attributes.taskIcon)
|
||||
.font(.caption)
|
||||
}
|
||||
.foregroundStyle(.green)
|
||||
}
|
||||
|
||||
DynamicIslandExpandedRegion(.trailing) {
|
||||
Image(systemName: "circle.fill")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.green)
|
||||
.padding(.trailing, 4)
|
||||
}
|
||||
|
||||
// MARK: Expanded – center (large timer)
|
||||
DynamicIslandExpandedRegion(.bottom) {
|
||||
Text(timerInterval: context.state.startDate...timerEndDate, countsDown: false)
|
||||
.font(.system(size: 44, weight: .bold, design: .monospaced))
|
||||
.foregroundStyle(.yellow)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.top, 4)
|
||||
}
|
||||
|
||||
} compactLeading: {
|
||||
Image(systemName: "timer")
|
||||
.foregroundStyle(.green)
|
||||
.font(.caption.weight(.semibold))
|
||||
|
||||
} compactTrailing: {
|
||||
Text(timerInterval: context.state.startDate...timerEndDate, countsDown: false)
|
||||
.monospacedDigit()
|
||||
.font(.caption2.weight(.medium))
|
||||
.foregroundStyle(.yellow)
|
||||
.frame(maxWidth: 52)
|
||||
|
||||
} minimal: {
|
||||
ZStack {
|
||||
Circle()
|
||||
.fill(LinearGradient(
|
||||
colors: [.green, Color(red: 0.8, green: 0.9, blue: 0.0)],
|
||||
startPoint: .topLeading,
|
||||
endPoint: .bottomTrailing
|
||||
))
|
||||
Text(context.attributes.taskIcon)
|
||||
.font(.system(size: 11))
|
||||
}
|
||||
}
|
||||
.keylineTint(.green)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
extension TimerActivityAttributes {
|
||||
fileprivate static var preview: TimerActivityAttributes {
|
||||
TimerActivityAttributes(taskName: "운동하기", taskIcon: "🏃")
|
||||
}
|
||||
}
|
||||
|
||||
extension TimerActivityAttributes.ContentState {
|
||||
fileprivate static var running: TimerActivityAttributes.ContentState {
|
||||
TimerActivityAttributes.ContentState(
|
||||
startDate: Date().addingTimeInterval(-125),
|
||||
elapsedSeconds: 125
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview("Notification", as: .content, using: TimerActivityAttributes.preview) {
|
||||
LemonLimeWidgetLiveActivity()
|
||||
} contentStates: {
|
||||
TimerActivityAttributes.ContentState.running
|
||||
}
|
||||
11
code_study/Baekjoon/c/1000.c
Executable file
11
code_study/Baekjoon/c/1000.c
Executable file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
|
||||
int a, b, n;
|
||||
scanf("%d %d",&a, &b);
|
||||
n = a + b;
|
||||
printf("%d", n);
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
code_study/Baekjoon/c/1001.c
Executable file
12
code_study/Baekjoon/c/1001.c
Executable file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
|
||||
int a, b;
|
||||
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("%d",a-b);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
24
code_study/Baekjoon/c/1003.c
Normal file
24
code_study/Baekjoon/c/1003.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int arr[41][2];
|
||||
arr[0][0] = 1;
|
||||
arr[0][1] = 0;
|
||||
arr[1][0] = 0;
|
||||
arr[1][1] = 1;
|
||||
for(int i=2; i<=40; i++){
|
||||
arr[i][0] = arr[i-1][0] + arr[i-2][0];
|
||||
arr[i][1] = arr[i-1][1] + arr[i-2][1];
|
||||
}
|
||||
|
||||
int T;
|
||||
scanf("%d",&T);
|
||||
|
||||
for(int i=0; i<T; i++){
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
printf("%d %d\n",arr[N][0],arr[N][1]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
13
code_study/Baekjoon/c/10039.c
Normal file
13
code_study/Baekjoon/c/10039.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int ans = 0;
|
||||
for(int i=0; i<5; i++) {
|
||||
int n;
|
||||
scanf("%d",&n);
|
||||
if(n < 40) n = 40;
|
||||
ans += n/5;
|
||||
}
|
||||
printf("%d\n",ans);
|
||||
return 0;
|
||||
}
|
||||
58
code_study/Baekjoon/c/1005_1.c
Normal file
58
code_study/Baekjoon/c/1005_1.c
Normal file
@ -0,0 +1,58 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int dp[1001];
|
||||
int build_info[1001][1001];
|
||||
int build_time[1001];
|
||||
int T, N, K, W;
|
||||
|
||||
int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
int solve(int target);
|
||||
|
||||
int main() {
|
||||
scanf("%d", &T);
|
||||
|
||||
int* ans = (int*)malloc(sizeof(int)*T);
|
||||
|
||||
for(int t=0; t<T; t++) {
|
||||
scanf("%d %d",&N, &K);
|
||||
|
||||
for(int i=1; i<=N; i++) {
|
||||
scanf("%d",&build_time[i]);
|
||||
build_info[i][0] = 0;
|
||||
dp[i] = -1;
|
||||
}
|
||||
|
||||
for(int i=0; i<K; i++) {
|
||||
int u, v;
|
||||
scanf("%d %d",&u, &v);
|
||||
build_info[v][++build_info[v][0]] = u;
|
||||
}
|
||||
|
||||
scanf("%d", &W);
|
||||
|
||||
ans[t] = solve(W);
|
||||
}
|
||||
|
||||
for(int t=0; t<T; t++) printf("%d\n", ans[t]);
|
||||
free(ans);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int solve(int target) {
|
||||
if(dp[target] == -1) {
|
||||
int max_built_time = 0;
|
||||
for(int i=1; i<=build_info[target][0]; i++) {
|
||||
int prev = build_info[target][i];
|
||||
max_built_time = max(max_built_time, solve(prev));
|
||||
}
|
||||
|
||||
dp[target] = max_built_time + build_time[target];
|
||||
}
|
||||
|
||||
return dp[target];
|
||||
}
|
||||
75
code_study/Baekjoon/c/1005_2.c
Normal file
75
code_study/Baekjoon/c/1005_2.c
Normal file
@ -0,0 +1,75 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int T, N, K, W;
|
||||
int dp[1001];
|
||||
int build_info[1001][1001];
|
||||
int build_time[1001];
|
||||
int indegree[1001];
|
||||
int q[1001];
|
||||
|
||||
int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
int solve();
|
||||
|
||||
int main() {
|
||||
scanf("%d", &T);
|
||||
|
||||
int* ans = (int*)malloc(sizeof(int)*T);
|
||||
|
||||
for(int t=0; t<T; t++) {
|
||||
scanf("%d %d",&N, &K);
|
||||
|
||||
for(int i=1; i<=N; i++) {
|
||||
scanf("%d",&build_time[i]);
|
||||
build_info[i][0] = 0;
|
||||
indegree[i] = 0;
|
||||
dp[i] = -1;
|
||||
}
|
||||
|
||||
for(int i=0; i<K; i++) {
|
||||
int u, v;
|
||||
scanf("%d %d",&u, &v);
|
||||
build_info[u][++build_info[u][0]] = v;
|
||||
indegree[v]++;
|
||||
}
|
||||
|
||||
scanf("%d", &W);
|
||||
|
||||
ans[t] = solve();
|
||||
}
|
||||
|
||||
for(int t=0; t<T; t++) printf("%d\n", ans[t]);
|
||||
free(ans);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int solve() {
|
||||
int front = 0, rear = 0;
|
||||
for(int i=1; i<=N; i++) {
|
||||
if(indegree[i] == 0) {
|
||||
q[rear++] = i;
|
||||
dp[i] = build_time[i];
|
||||
}
|
||||
}
|
||||
|
||||
while(front != rear) {
|
||||
int now = q[front++];
|
||||
|
||||
if(now == W) break;
|
||||
|
||||
for(int i=1; i<=build_info[now][0]; i++) {
|
||||
int next = build_info[now][i];
|
||||
|
||||
dp[next] = max(dp[next], dp[now] + build_time[next]);
|
||||
|
||||
indegree[next]--;
|
||||
if(indegree[next] == 0) q[rear++] = next;
|
||||
}
|
||||
}
|
||||
|
||||
return dp[W];
|
||||
}
|
||||
9
code_study/Baekjoon/c/1008.c
Executable file
9
code_study/Baekjoon/c/1008.c
Executable file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int a, b;
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("%.9f",(double)a/b);
|
||||
return 0;
|
||||
|
||||
}
|
||||
21
code_study/Baekjoon/c/10101.c
Normal file
21
code_study/Baekjoon/c/10101.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int a, b, c, sum=0;
|
||||
scanf("%d %d %d",&a,&b,&c);
|
||||
sum = a + b + c;
|
||||
if(sum!=180) {
|
||||
printf("Error\n");
|
||||
}
|
||||
else if(a==b && a==c) {
|
||||
printf("Equilateral\n");
|
||||
}
|
||||
else if(a==b || a==c || b==c) {
|
||||
printf("Isosceles\n");
|
||||
}
|
||||
else {
|
||||
printf("Scalene\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
code_study/Baekjoon/c/10170.c
Normal file
6
code_study/Baekjoon/c/10170.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
printf("NFC West W L T\n-----------------------\nSeattle 13 3 0\nSan Francisco 12 4 0\nArizona 10 6 0\nSt. Louis 7 9 0\n\nNFC North W L T\n-----------------------\nGreen Bay 8 7 1\nChicago 8 8 0\nDetroit 7 9 0\nMinnesota 5 10 1\n");
|
||||
return 0;
|
||||
}
|
||||
41
code_study/Baekjoon/c/1018.c
Normal file
41
code_study/Baekjoon/c/1018.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
char board[51][51];
|
||||
int N, M, cnt1, cnt2, min, res=64;
|
||||
scanf("%d %d",&N,&M);
|
||||
for(int i=0; i<N; i++) {
|
||||
scanf("%s",board[i]);
|
||||
}
|
||||
|
||||
for(int i=0; i<N-7; i++) {
|
||||
for(int j=0; j<M-7; j++) {
|
||||
cnt1 = 0;
|
||||
cnt2 = 0;
|
||||
for(int n=0; n<8; n++) {
|
||||
for(int m=0; m<8; m++) {
|
||||
if((n+m)%2==0){
|
||||
if(board[i+n][j+m]!='W') cnt1++;
|
||||
if(board[i+n][j+m]!='B') cnt2++;
|
||||
}
|
||||
else{
|
||||
if(board[i+n][j+m]!='B') cnt1++;
|
||||
if(board[i+n][j+m]!='W') cnt2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
min = cnt1>cnt2 ? cnt2 : cnt1;
|
||||
if(min<res) {
|
||||
if(min==0) {
|
||||
printf("0\n");
|
||||
return 0;
|
||||
}
|
||||
res = min;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
17
code_study/Baekjoon/c/10250.c
Normal file
17
code_study/Baekjoon/c/10250.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int T;
|
||||
scanf("%d",&T);
|
||||
while(T--) {
|
||||
int H, W, N, floor, number, isMulti;
|
||||
scanf("%d %d %d",&H, &W, &N);
|
||||
isMulti = N%H;
|
||||
floor = isMulti ? N%H : H;
|
||||
number = isMulti ? N/H + 1 : N/H;
|
||||
if(number<10) printf("%d0%d\n",floor,number);
|
||||
else printf("%d%d\n",floor,number);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
8
code_study/Baekjoon/c/10430.c
Executable file
8
code_study/Baekjoon/c/10430.c
Executable file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int a, b, c;
|
||||
scanf("%d %d %d",&a, &b, &c);
|
||||
printf("%d %d %d %d",(a+b)%c,((a%c)+(b%c))%c,(a*b)%c,((a%c)*(b%c))%c);
|
||||
return 0;
|
||||
}
|
||||
38
code_study/Baekjoon/c/10757.c
Normal file
38
code_study/Baekjoon/c/10757.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void reverse(char arr[]) {
|
||||
int len = strlen(arr);
|
||||
for (int i = 0; i < len / 2; i++) {
|
||||
char temp = arr[i];
|
||||
arr[i] = arr[len - i - 1];
|
||||
arr[len - i - 1] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char A[10002] = { 0 }, B[10002] = { 0 }, res[10003] = { 0 };
|
||||
int carry = 0;
|
||||
|
||||
scanf("%s %s", A, B);
|
||||
reverse(A);
|
||||
reverse(B);
|
||||
|
||||
int len = strlen(A) > strlen(B) ? strlen(A) : strlen(B);
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
int sum = A[i] - '0' + B[i] - '0' + carry;
|
||||
|
||||
while (sum < 0) sum += '0';
|
||||
if (sum > 9) carry = 1;
|
||||
else carry = 0;
|
||||
res[i] = sum % 10 + '0';
|
||||
}
|
||||
|
||||
if (carry == 1) res[len] = '1';
|
||||
reverse(res);
|
||||
printf("%s", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
41
code_study/Baekjoon/c/10775.c
Normal file
41
code_study/Baekjoon/c/10775.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX 100000
|
||||
|
||||
int G, P, gi, res;
|
||||
int parent[MAX+1];
|
||||
|
||||
void init_uf();
|
||||
int find(int x);
|
||||
void Union(int x);
|
||||
|
||||
int main() {
|
||||
scanf("%d %d", &G, &P);
|
||||
init_uf();
|
||||
|
||||
for(int i=0; i<P; i++) {
|
||||
scanf("%d",&gi);
|
||||
|
||||
if(find(gi) == 0) break;
|
||||
|
||||
Union(gi);
|
||||
res++;
|
||||
}
|
||||
|
||||
printf("%d\n", res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void init_uf() {
|
||||
for(int i=0; i<=G; i++) parent[i] = i;
|
||||
}
|
||||
|
||||
int find(int x) {
|
||||
if(x != parent[x]) return parent[x] = find(parent[x]);
|
||||
return parent[x];
|
||||
}
|
||||
|
||||
void Union(int x) {
|
||||
parent[find(x)] = find(x)-1;
|
||||
}
|
||||
28
code_study/Baekjoon/c/10798.c
Normal file
28
code_study/Baekjoon/c/10798.c
Normal file
@ -0,0 +1,28 @@
|
||||
#include <stdio.h>
|
||||
|
||||
char arr[5][16];
|
||||
|
||||
int main(){
|
||||
int max_len=0, len;
|
||||
|
||||
for(int i=0; i<5; i++){
|
||||
scanf("%s",arr[i]);
|
||||
len=0;
|
||||
for(int j=0; arr[i][j]!='\0'; j++){
|
||||
len++;
|
||||
}
|
||||
if(max_len<len){
|
||||
max_len=len;
|
||||
}
|
||||
}
|
||||
|
||||
for(int j=0; j<max_len; j++){
|
||||
for(int i=0; i<5; i++){
|
||||
if(arr[i][j] != '\0'){
|
||||
printf("%c",arr[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
code_study/Baekjoon/c/10807.c
Normal file
15
code_study/Baekjoon/c/10807.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int arr[100];
|
||||
int n, v, cnt=0;
|
||||
scanf("%d",&n);
|
||||
for(int i=0; i<n; i++){
|
||||
scanf("%d",&arr[i]);
|
||||
}
|
||||
scanf("%d",&v);
|
||||
for(int i=0; i<n; i++){
|
||||
if(arr[i]==v) cnt++;
|
||||
}
|
||||
printf("%d",cnt);
|
||||
return 0;
|
||||
}
|
||||
17
code_study/Baekjoon/c/10808.c
Normal file
17
code_study/Baekjoon/c/10808.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int count[26];
|
||||
|
||||
int main() {
|
||||
char s[101];
|
||||
scanf("%s", s);
|
||||
|
||||
int i=0;
|
||||
while(s[i] !='\0') {
|
||||
count[s[i++] - 'a']++;
|
||||
}
|
||||
|
||||
for(int j=0; j<26; j++) printf("%d ",count[j]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
18
code_study/Baekjoon/c/10809.c
Normal file
18
code_study/Baekjoon/c/10809.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int arr[26] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};
|
||||
int temp=0;
|
||||
char s[101];
|
||||
scanf("%s",s);
|
||||
for(int i=0; s[i]!='\0'; i++){
|
||||
temp = (int)(s[i]-'a');
|
||||
if(arr[temp]==-1) arr[temp]=i;
|
||||
}
|
||||
|
||||
for(int i=0; i<26; i++){
|
||||
printf("%d ",arr[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
18
code_study/Baekjoon/c/10810.c
Normal file
18
code_study/Baekjoon/c/10810.c
Normal file
@ -0,0 +1,18 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, m, i, j, k;
|
||||
int arr[100]={0};
|
||||
|
||||
scanf("%d %d",&n, &m);
|
||||
|
||||
while(m--){
|
||||
scanf("%d %d %d",&i,&j,&k);
|
||||
for(int x =i-1;x<j;x++){
|
||||
arr[x] = k;
|
||||
}
|
||||
}
|
||||
|
||||
for(int t=0; t<n; t++){
|
||||
printf("%d ",arr[t]);
|
||||
}
|
||||
}
|
||||
21
code_study/Baekjoon/c/10811.c
Normal file
21
code_study/Baekjoon/c/10811.c
Normal file
@ -0,0 +1,21 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int arr[100];
|
||||
int m,n,x,i,j,t;
|
||||
scanf("%d %d",&n, &m);
|
||||
|
||||
for(x=0; x<n; x++) arr[x] = x+1;
|
||||
|
||||
while(m--){
|
||||
scanf("%d %d",&i,&j);
|
||||
for(x=0;x<(j-i+1)/2;x++){
|
||||
t = arr[i+x-1];
|
||||
arr[i+x-1] = arr[j-x-1];
|
||||
arr[j-x-1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
for(x=0; x<n; x++) printf("%d ",arr[x]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
22
code_study/Baekjoon/c/10813.c
Normal file
22
code_study/Baekjoon/c/10813.c
Normal file
@ -0,0 +1,22 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, m, x, y, t;
|
||||
scanf("%d %d",&n, &m);
|
||||
|
||||
int arr[100];
|
||||
for(int i=0; i<n; i++) arr[i] = i+1;
|
||||
|
||||
while(m--){
|
||||
scanf("%d %d",&x, &y);
|
||||
t = arr[x-1];
|
||||
arr[x-1] = arr[y-1];
|
||||
arr[y-1] = t;
|
||||
}
|
||||
|
||||
for(int j=0; j<n; j++){
|
||||
printf("%d ",arr[j]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
25
code_study/Baekjoon/c/10818.c
Normal file
25
code_study/Baekjoon/c/10818.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
int main(){
|
||||
int n;
|
||||
int min, max;
|
||||
|
||||
scanf("%d",&n);
|
||||
int *arr = malloc(sizeof(int)*n);
|
||||
|
||||
for(int i=0; i<n; i++){
|
||||
scanf("%d",&arr[i]);
|
||||
}
|
||||
|
||||
min = arr[0];
|
||||
max = min;
|
||||
|
||||
for(int j=1; j<n; j++){
|
||||
if (arr[j]<min) min=arr[j];
|
||||
if (arr[j]>max) max=arr[j];
|
||||
}
|
||||
free(arr);
|
||||
printf("%d %d",min, max);
|
||||
|
||||
return 0;
|
||||
}
|
||||
60
code_study/Baekjoon/c/10828.c
Normal file
60
code_study/Baekjoon/c/10828.c
Normal file
@ -0,0 +1,60 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int opcodeToNum(char* opcode){
|
||||
if(!strcmp(opcode, "push")) return 0;
|
||||
if(!strcmp(opcode, "pop")) return 1;
|
||||
if(!strcmp(opcode, "size")) return 2;
|
||||
if(!strcmp(opcode, "empty")) return 3;
|
||||
if(!strcmp(opcode, "top")) return 4;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
int* stack = (int*)malloc(sizeof(int)* N);
|
||||
memset(stack,0,sizeof(int)*N);
|
||||
|
||||
int len = 0;
|
||||
while(N--){
|
||||
char str[13];
|
||||
if (fgets(str, sizeof(str), stdin) == NULL) break;
|
||||
if (str[0] == '\n') { N++; continue; }
|
||||
str[strcspn(str, "\n")] = 0;
|
||||
char *opcode = strtok(str, " ");
|
||||
char *operend = strtok(NULL, " ");
|
||||
int opNum = opcodeToNum(opcode);
|
||||
int operendNum = operend ? atoi(operend) : 0;
|
||||
|
||||
switch (opNum)
|
||||
{
|
||||
case 0:
|
||||
stack[len] = operendNum;
|
||||
len++;
|
||||
break;
|
||||
case 1:
|
||||
if(len==0) {
|
||||
printf("-1\n");
|
||||
}
|
||||
else {
|
||||
printf("%d\n",stack[--len]);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
printf("%d\n",len);
|
||||
break;
|
||||
case 3:
|
||||
printf("%d\n", len ? 0 : 1);
|
||||
break;
|
||||
case 4:
|
||||
printf("%d\n", len ? stack[len-1] : -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(stack);
|
||||
return 0;
|
||||
}
|
||||
98
code_study/Baekjoon/c/10830.c
Normal file
98
code_study/Baekjoon/c/10830.c
Normal file
@ -0,0 +1,98 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int** allocation(int N, bool input);
|
||||
void free_memory(int** arr, int N);
|
||||
void printArr(int** arr, int N);
|
||||
int** multyply(int** A, int** B, int N);
|
||||
int** product(int** arr, int N, long long B);
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
long long B;
|
||||
scanf("%d %lld", &N, &B);
|
||||
int** arr = allocation(N, true);
|
||||
int** result = product(arr, N, B);
|
||||
|
||||
printArr(result, N);
|
||||
|
||||
free_memory(arr, N);
|
||||
free_memory(result, N);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int** allocation(int N, bool input) {
|
||||
int** arr = (int**)malloc(sizeof(int*)*N);
|
||||
|
||||
for(int i=0; i<N; i++) {
|
||||
arr[i] = (int*)malloc(sizeof(int)*N);
|
||||
|
||||
if(input) {
|
||||
for(int n=0; n<N; n++) scanf("%d", &arr[i][n]);
|
||||
}
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void free_memory(int** arr, int N) {
|
||||
for(int i=0; i<N; i++) free(arr[i]);
|
||||
free(arr);
|
||||
}
|
||||
|
||||
void printArr(int** arr, int N) {
|
||||
for(int i=0; i<N; i++) {
|
||||
for(int j=0; j<N; j++) {
|
||||
printf("%d ",arr[i][j]);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
int** multyply(int** A, int** B, int N) {
|
||||
int** result = allocation(N, false);
|
||||
|
||||
for(int y=0; y<N; y++) {
|
||||
for(int x=0; x<N; x++) {
|
||||
result[y][x] = 0;
|
||||
|
||||
for(int i=0; i<N; i++) {
|
||||
result[y][x] += A[y][i] * B[i][x];
|
||||
}
|
||||
|
||||
result[y][x] %= 1000;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int** product(int** arr, int N, long long B) {
|
||||
if (B==1) {
|
||||
int** basedArr = allocation(N, false);
|
||||
for(int y=0; y<N; y++) {
|
||||
for(int x=0; x<N; x++) {
|
||||
basedArr[y][x] = arr[y][x] % 1000;
|
||||
}
|
||||
}
|
||||
|
||||
return basedArr;
|
||||
}
|
||||
|
||||
int** half = product(arr, N, B/2);
|
||||
int** temp = multyply(half, half, N);
|
||||
free_memory(half, N);
|
||||
|
||||
if (B%2 == 1) {
|
||||
int** result = multyply(temp, arr, N);
|
||||
free_memory(temp, N);
|
||||
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
39
code_study/Baekjoon/c/10844.c
Normal file
39
code_study/Baekjoon/c/10844.c
Normal file
@ -0,0 +1,39 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define MOD 1000000000
|
||||
long long dp[101][10];
|
||||
|
||||
int main() {
|
||||
for (int i=1; i<10; i++) dp[1][i] = 1;
|
||||
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
|
||||
for (int n=1; n<N; n++) {
|
||||
for (int num=0; num<10; num++) {
|
||||
|
||||
if (dp[n][num] == 0) continue;
|
||||
|
||||
int next_num = num - 1;
|
||||
if (0<=next_num && next_num <=9) {
|
||||
dp[n+1][next_num] += dp[n][num];
|
||||
dp[n+1][next_num] %= MOD;
|
||||
}
|
||||
|
||||
next_num = num + 1;
|
||||
if (0<=next_num && next_num <=9) {
|
||||
dp[n+1][next_num] += dp[n][num];
|
||||
dp[n+1][next_num] %= MOD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long long result = 0;
|
||||
for(int i=0; i<10; i++) {
|
||||
result += dp[N][i];
|
||||
result %= MOD;
|
||||
}
|
||||
printf("%lld\n",result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
65
code_study/Baekjoon/c/10845.c
Normal file
65
code_study/Baekjoon/c/10845.c
Normal file
@ -0,0 +1,65 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int opcodeToNum(char* opcode);
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
scanf("%d\n",&N);
|
||||
int queue[10000];
|
||||
int front = 0, rear = 0, size = 0;
|
||||
|
||||
while(N--){
|
||||
char opcode[6];
|
||||
int operend;
|
||||
scanf("%s",opcode);
|
||||
|
||||
switch(opcodeToNum(opcode))
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
scanf("%d\n",&operend);
|
||||
queue[rear++] = operend;
|
||||
size++;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
printf("%d\n", size ? queue[front++] : -1);
|
||||
size = size ? size - 1 : 0;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
printf("%d\n",size);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
printf("%d\n", size ? 0 : 1);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
printf("%d\n", size ? queue[front] : -1);
|
||||
break;
|
||||
}
|
||||
case 5:
|
||||
{
|
||||
printf("%d\n", size ? queue[rear-1] : -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int opcodeToNum(char* opcode){
|
||||
if(!strcmp(opcode, "push")) return 0;
|
||||
else if(!strcmp(opcode, "pop")) return 1;
|
||||
else if(!strcmp(opcode, "size")) return 2;
|
||||
else if(!strcmp(opcode, "empty")) return 3;
|
||||
else if(!strcmp(opcode, "front")) return 4;
|
||||
else if(!strcmp(opcode, "back")) return 5;
|
||||
return -1;
|
||||
}
|
||||
11
code_study/Baekjoon/c/1085.c
Normal file
11
code_study/Baekjoon/c/1085.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int x,y,w,h,i,j;
|
||||
scanf("%d %d %d %d",&x,&y, &w, &h);
|
||||
i = (w-x) < x ? (w-x) : x;
|
||||
j = (h-y) < y ? (h-y) : y;
|
||||
printf("%d\n", i<j ? i : j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
8
code_study/Baekjoon/c/10869.c
Executable file
8
code_study/Baekjoon/c/10869.c
Executable file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int a, b;
|
||||
scanf("%d %d",&a,&b);
|
||||
printf("%d %d %d %d %d",a+b,a-b,a*b,a/b,a%b);
|
||||
return 0;
|
||||
}
|
||||
13
code_study/Baekjoon/c/10871.c
Normal file
13
code_study/Baekjoon/c/10871.c
Normal file
@ -0,0 +1,13 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n,x;
|
||||
scanf("%d %d",&n,&x);
|
||||
int arr[10000];
|
||||
for(int i=0; i<n; i++){
|
||||
scanf("%d",&arr[i]);
|
||||
}
|
||||
for(int j=0; j<n; j++){
|
||||
if(arr[j]<x) printf("%d ",arr[j]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
8
code_study/Baekjoon/c/10926.c
Executable file
8
code_study/Baekjoon/c/10926.c
Executable file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
char a[50];
|
||||
scanf("%s",a);
|
||||
printf("%s??\!",a);
|
||||
return 0;
|
||||
}
|
||||
10
code_study/Baekjoon/c/10950.c
Normal file
10
code_study/Baekjoon/c/10950.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, a, b;
|
||||
scanf("%d",&n);
|
||||
for(int i=0; i<n; i++){
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("%d\n",a+b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
6
code_study/Baekjoon/c/10951.c
Normal file
6
code_study/Baekjoon/c/10951.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int a,b;
|
||||
while(scanf("%d %d",&a,&b) != EOF) printf("%d\n",a+b);
|
||||
return 0;
|
||||
}
|
||||
11
code_study/Baekjoon/c/10952.c
Normal file
11
code_study/Baekjoon/c/10952.c
Normal file
@ -0,0 +1,11 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int a,b;
|
||||
|
||||
while(1){
|
||||
scanf("%d %d",&a,&b);
|
||||
if(a==b&&a==0) break;
|
||||
printf("%d\n",a+b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
23
code_study/Baekjoon/c/10988.c
Normal file
23
code_study/Baekjoon/c/10988.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
char s[101];
|
||||
scanf("%s",s);
|
||||
|
||||
int l=0;
|
||||
for(int i=0; s[i]!='\0'; i++){
|
||||
l++;
|
||||
}
|
||||
|
||||
int flag = 1;
|
||||
for(int i=0; i<l/2; i++){
|
||||
if(s[i]!=s[l-i-1]){
|
||||
flag = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d",flag);
|
||||
|
||||
return 0;
|
||||
}
|
||||
24
code_study/Baekjoon/c/10989.c
Normal file
24
code_study/Baekjoon/c/10989.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int count[10000] = {0,};
|
||||
int n, temp;
|
||||
scanf("%d",&n);
|
||||
for(int i=0; i<n; i++) {
|
||||
scanf("%d",&temp);
|
||||
count[temp-1]++;
|
||||
}
|
||||
|
||||
int idx=0, cnt=0;
|
||||
while(cnt!=n) {
|
||||
if(count[idx]!=0) {
|
||||
for(int i=0; i<count[idx]; i++) {
|
||||
printf("%d\n",idx+1);
|
||||
}
|
||||
cnt += count[idx];
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
code_study/Baekjoon/c/10998.c
Executable file
12
code_study/Baekjoon/c/10998.c
Executable file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
|
||||
int a, b;
|
||||
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("%d",a*b);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
27
code_study/Baekjoon/c/11005.c
Normal file
27
code_study/Baekjoon/c/11005.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
int B, val, i=0;
|
||||
long int N;
|
||||
char res[31];
|
||||
|
||||
scanf("%ld %d", &N, &B);
|
||||
|
||||
while(N!=0){
|
||||
val = N%B;
|
||||
N/=B;
|
||||
if(val>9){
|
||||
res[i]='A'+val-10;
|
||||
}
|
||||
else{
|
||||
res[i]='0'+val;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
while(i--){
|
||||
printf("%c",res[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
10
code_study/Baekjoon/c/11021.c
Normal file
10
code_study/Baekjoon/c/11021.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, a, b;
|
||||
scanf("%d",&n);
|
||||
for(int i=1; i<=n; i++){
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("Case #%d: %d\n",i,a+b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
10
code_study/Baekjoon/c/11022.c
Normal file
10
code_study/Baekjoon/c/11022.c
Normal file
@ -0,0 +1,10 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, a, b;
|
||||
scanf("%d",&n);
|
||||
for(int i=1; i<=n; i++){
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("Case #%d: %d + %d = %d\n",i,a,b,a+b);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
23
code_study/Baekjoon/c/11047.c
Normal file
23
code_study/Baekjoon/c/11047.c
Normal file
@ -0,0 +1,23 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
int N, K;
|
||||
scanf("%d %d",&N, &K);
|
||||
int* coin = (int*)malloc(sizeof(int)*N);
|
||||
for(int i=0; i<N; i++){
|
||||
scanf("%d",&coin[i]);
|
||||
}
|
||||
|
||||
int k_diff = K, count = 0;
|
||||
for(int i=N-1; i>=0; i--){
|
||||
if(coin[i]<=k_diff){
|
||||
count += k_diff/coin[i];
|
||||
k_diff = k_diff%coin[i];
|
||||
}
|
||||
}
|
||||
printf("%d\n",count);
|
||||
|
||||
free(coin);
|
||||
return 0;
|
||||
}
|
||||
34
code_study/Baekjoon/c/11049.c
Normal file
34
code_study/Baekjoon/c/11049.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define MAX INT_MAX
|
||||
|
||||
int size[501][2];
|
||||
int dp[501][501];
|
||||
|
||||
int min(int a, int b) { return a>b ? b : a; }
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
|
||||
for(int i=1; i<=N; i++) scanf("%d %d",&size[i][0], &size[i][1]);
|
||||
|
||||
for(int len=1; len<N; len++) {
|
||||
for(int i=1; i<=N-len; i++) {
|
||||
int j = i + len;
|
||||
dp[i][j] = MAX;
|
||||
|
||||
for(int k=i; k<j; k++) {
|
||||
int left_times = dp[i][k];
|
||||
int right_times = dp[k+1][j];
|
||||
int operator_count = size[i][0] * size[k][1] * size[j][1];
|
||||
dp[i][j] = min(dp[i][j], left_times + right_times + operator_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d\n",dp[1][N]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
15
code_study/Baekjoon/c/11050.c
Normal file
15
code_study/Baekjoon/c/11050.c
Normal file
@ -0,0 +1,15 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int n, k, result=1, factR=1;
|
||||
scanf("%d %d", &n, &k);
|
||||
|
||||
for(int i=1; i<=k; i++) {
|
||||
result *= n-i+1;
|
||||
factR *= i;
|
||||
}
|
||||
|
||||
printf("%d\n",result/factR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
30
code_study/Baekjoon/c/11053.c
Normal file
30
code_study/Baekjoon/c/11053.c
Normal file
@ -0,0 +1,30 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX 1000
|
||||
|
||||
int A[MAX], dp[MAX];
|
||||
int N, ans;
|
||||
|
||||
int max(int a, int b) {
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
int main() {
|
||||
scanf("%d", &N);
|
||||
for(int i=0; i<N; i++) {
|
||||
scanf("%d",&A[i]);
|
||||
dp[i] = 1;
|
||||
}
|
||||
|
||||
for(int i=1; i<N; i++) {
|
||||
for(int j=0; j<i; j++) {
|
||||
if(A[i] > A[j]) dp[i] = max(dp[i], dp[j] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i=0; i<N; i++) ans = max(dp[i], ans);
|
||||
|
||||
printf("%d\n", ans);
|
||||
|
||||
return 0;
|
||||
}
|
||||
56
code_study/Baekjoon/c/11054.c
Normal file
56
code_study/Baekjoon/c/11054.c
Normal file
@ -0,0 +1,56 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
void freeAll(int* a, int* b, int* c, int* d, int* e) {
|
||||
free(a); free(b); free(c); free(d); free(e);
|
||||
}
|
||||
|
||||
int max(int a, int b) {
|
||||
return a>b ? a : b;
|
||||
}
|
||||
|
||||
int maxInArr(int* arr, int N) {
|
||||
int maxNum = 0;
|
||||
for(int i=0; i<N; i++) maxNum = max(maxNum, arr[i]);
|
||||
|
||||
return maxNum;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
|
||||
int* arr = (int*)malloc(sizeof(int)*N);
|
||||
int* arrR = (int*)malloc(sizeof(int)*N);
|
||||
for(int i=0; i<N; i++) {
|
||||
int num;
|
||||
scanf("%d",&num);
|
||||
arr[i] = num;
|
||||
arrR[N-i-1] = num;
|
||||
}
|
||||
|
||||
int* dp = (int*)malloc(sizeof(int)*N);
|
||||
int* dpR = (int*)malloc(sizeof(int)*N);
|
||||
|
||||
for(int i=0; i<N; i++) {
|
||||
dp[i] = 1;
|
||||
dpR[i] = 1;
|
||||
}
|
||||
|
||||
for(int i=0; i<N; i++) {
|
||||
for(int j=0; j<i; j++) {
|
||||
if(arr[i] > arr[j]) dp[i] = max(dp[i], dp[j]+1);
|
||||
if(arrR[i] > arrR[j]) dpR[i] = max(dpR[i], dpR[j]+1);
|
||||
}
|
||||
}
|
||||
|
||||
int* length = (int*)malloc(sizeof(int)*N);
|
||||
|
||||
for(int i=0; i<N; i++) length[i] = dp[i] + dpR[N-1-i] - 1;
|
||||
|
||||
printf("%d\n", maxInArr(length, N));
|
||||
|
||||
freeAll(arr, arrR, dp, dpR, length);
|
||||
return 0;
|
||||
}
|
||||
31
code_study/Baekjoon/c/1106.c
Normal file
31
code_study/Baekjoon/c/1106.c
Normal file
@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_COST 100000
|
||||
|
||||
int min(int a, int b) {
|
||||
return a > b ? b : a;
|
||||
}
|
||||
|
||||
int dp[1101];
|
||||
|
||||
int main() {
|
||||
int C, N;
|
||||
scanf("%d %d",&C, &N);
|
||||
|
||||
dp[0] = 0;
|
||||
for(int i=1; i<=C+100; i++) dp[i] = MAX_COST;
|
||||
|
||||
while(N--) {
|
||||
int cost, man;
|
||||
scanf("%d %d",&cost, &man);
|
||||
|
||||
for(int c=man; c<C+101; c++) dp[c] = min(dp[c], dp[c-man] + cost);
|
||||
}
|
||||
|
||||
int ans = MAX_COST;
|
||||
for(int i=C; i<C+101; i++) ans = min(ans, dp[i]);
|
||||
|
||||
printf("%d\n",ans);
|
||||
|
||||
return 0;
|
||||
}
|
||||
73
code_study/Baekjoon/c/11286.c
Normal file
73
code_study/Baekjoon/c/11286.c
Normal file
@ -0,0 +1,73 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
void swap(int *a, int *b);
|
||||
void insert(int value, int *heap, int* size);
|
||||
void delete(int *heap, int* size);
|
||||
|
||||
int main(){
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
int *heap = (int*)malloc(sizeof(int)*(N+1));
|
||||
int heap_size = 0;
|
||||
while(N--){
|
||||
int op;
|
||||
scanf("%d",&op);
|
||||
if(op==0) delete(heap, &heap_size);
|
||||
else insert(op, heap, &heap_size);
|
||||
}
|
||||
|
||||
free(heap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void swap(int *a, int *b){
|
||||
int temp = *a;
|
||||
*a = *b;
|
||||
*b = temp;
|
||||
}
|
||||
|
||||
void insert(int value, int *heap, int* size){
|
||||
heap[++(*size)] = value;
|
||||
int current = *size;
|
||||
while(current != 1){
|
||||
if((abs(heap[current]) < abs(heap[current/2]) ||
|
||||
(abs(heap[current]) == abs(heap[current/2]) && heap[current]<heap[current/2]))){
|
||||
swap(&heap[current], &heap[current/2]);
|
||||
current /= 2;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void delete(int *heap, int* size){
|
||||
if(*size==0){
|
||||
printf("0\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf("%d\n",heap[1]);
|
||||
heap[1] = heap[(*size)--];
|
||||
|
||||
int current = 1;
|
||||
while (current * 2 <= *size) {
|
||||
int child = current * 2;
|
||||
|
||||
if (child + 1 <= *size &&
|
||||
((abs(heap[child + 1]) < abs(heap[child])) ||
|
||||
(abs(heap[child + 1]) == abs(heap[child]) && heap[child + 1] < heap[child]))) {
|
||||
child++;
|
||||
}
|
||||
|
||||
if ((abs(heap[child]) < abs(heap[current])) ||
|
||||
(abs(heap[child]) == abs(heap[current]) && heap[child] < heap[current])) {
|
||||
swap(&heap[current], &heap[child]);
|
||||
current = child;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
code_study/Baekjoon/c/11382.c
Executable file
8
code_study/Baekjoon/c/11382.c
Executable file
@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
long int a, b, c;
|
||||
scanf("%ld %ld %ld",&a, &b, &c);
|
||||
printf("%ld",a+b+c);
|
||||
return 0;
|
||||
}
|
||||
71
code_study/Baekjoon/c/11444.c
Normal file
71
code_study/Baekjoon/c/11444.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <stdio.h>
|
||||
// Fn | 1 1 | ^n-1 F1(1)
|
||||
// Fn-1 | 1 0 | F0(0)
|
||||
|
||||
#define MOD 1000000007
|
||||
|
||||
typedef long long matrix[2][2];
|
||||
|
||||
void multyply_matrix(matrix A, matrix B, matrix result);
|
||||
void power_matrix(matrix A, long long exp, matrix result);
|
||||
|
||||
int main() {
|
||||
long long n;
|
||||
scanf("%lld", &n);
|
||||
|
||||
if (n <= 1) {
|
||||
printf("%lld\n", n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
matrix A = {{1, 1}, {1, 0}};
|
||||
matrix result_matrix;
|
||||
|
||||
power_matrix(A, n - 1, result_matrix);
|
||||
|
||||
printf("%lld\n", result_matrix[0][0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void multyply_matrix(matrix A, matrix B, matrix result) {
|
||||
matrix temp;
|
||||
temp[0][0] = (A[0][0] * B[0][0] + A[0][1] * B[1][0]) % MOD;
|
||||
temp[0][1] = (A[0][0] * B[0][1] + A[0][1] * B[1][1]) % MOD;
|
||||
temp[1][0] = (A[1][0] * B[0][0] + A[1][1] * B[1][0]) % MOD;
|
||||
temp[1][1] = (A[1][0] * B[0][1] + A[1][1] * B[1][1]) % MOD;
|
||||
|
||||
result[0][0] = temp[0][0];
|
||||
result[0][1] = temp[0][1];
|
||||
result[1][0] = temp[1][0];
|
||||
result[1][1] = temp[1][1];
|
||||
}
|
||||
|
||||
void power_matrix(matrix A, long long exp, matrix result) {
|
||||
if (exp == 0) {
|
||||
result[0][0] = 1; result[0][1] = 0;
|
||||
result[1][0] = 0; result[1][1] = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (exp == 1) {
|
||||
result[0][0] = A[0][0]; result[0][1] = A[0][1];
|
||||
result[1][0] = A[1][0]; result[1][1] = A[1][1];
|
||||
return;
|
||||
}
|
||||
|
||||
matrix half;
|
||||
power_matrix(A, exp / 2, half);
|
||||
|
||||
matrix temp;
|
||||
multyply_matrix(half, half, temp);
|
||||
|
||||
if (exp % 2 == 1) {
|
||||
multyply_matrix(temp, A, result);
|
||||
} else {
|
||||
result[0][0] = temp[0][0];
|
||||
result[0][1] = temp[0][1];
|
||||
result[1][0] = temp[1][0];
|
||||
result[1][1] = temp[1][1];
|
||||
}
|
||||
}
|
||||
19
code_study/Baekjoon/c/1152.c
Normal file
19
code_study/Baekjoon/c/1152.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
char s[1000001];
|
||||
scanf("%[^\n]",s);
|
||||
|
||||
int n=0;
|
||||
|
||||
if(s[0]!=' ') n=1;
|
||||
|
||||
for(int i=0; s[i+1]!='\0'; i++){
|
||||
if((s[i]==' ') && (s[i+1]!=' ')) n++;
|
||||
}
|
||||
|
||||
printf("%d",n);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
32
code_study/Baekjoon/c/1157.c
Normal file
32
code_study/Baekjoon/c/1157.c
Normal file
@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
int arr[26] = {};
|
||||
char s[1000001];
|
||||
|
||||
scanf("%s",s);
|
||||
|
||||
for(int i=0; s[i]!='\0'; i++){
|
||||
if(s[i] >= 'a' && s[i] <= 'z'){
|
||||
s[i]=s[i]-32;
|
||||
}
|
||||
arr[s[i]-'A']++;
|
||||
}
|
||||
|
||||
int max_idx=0, max=0, dup;
|
||||
|
||||
for(int i=0; i<26; i++){
|
||||
if(arr[i]>max){
|
||||
max=arr[i];
|
||||
max_idx=i;
|
||||
dup=0;
|
||||
} else if(arr[i]==max && max!=0){
|
||||
dup=1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%c", !dup ? 'A'+max_idx : '?');
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
19
code_study/Baekjoon/c/11653.c
Normal file
19
code_study/Baekjoon/c/11653.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main() {
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
|
||||
int v=2;
|
||||
while(v<=N) {
|
||||
if(N%v==0) {
|
||||
printf("%d\n",v);
|
||||
N/=v;
|
||||
}
|
||||
else {
|
||||
v++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
7
code_study/Baekjoon/c/11654.c
Normal file
7
code_study/Baekjoon/c/11654.c
Normal file
@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
char c;
|
||||
c = getchar();
|
||||
printf("%d",c);
|
||||
return 0;
|
||||
}
|
||||
24
code_study/Baekjoon/c/11659.c
Normal file
24
code_study/Baekjoon/c/11659.c
Normal file
@ -0,0 +1,24 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
int N, M;
|
||||
scanf("%d %d",&N, &M);
|
||||
int* accSum = (int*)malloc(sizeof(int)*(N+1));
|
||||
accSum[0] = 0;
|
||||
for(int i=1; i<=N; i++){
|
||||
int n;
|
||||
scanf("%d",&n);
|
||||
accSum[i] += accSum[i-1] + n;
|
||||
}
|
||||
|
||||
while(M--){
|
||||
int a, b;
|
||||
scanf("%d %d",&a, &b);
|
||||
printf("%d\n",accSum[b]-accSum[a-1]);
|
||||
}
|
||||
|
||||
free(accSum);
|
||||
|
||||
return 0;
|
||||
}
|
||||
31
code_study/Baekjoon/c/11660.c
Normal file
31
code_study/Baekjoon/c/11660.c
Normal file
@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
int N, M;
|
||||
scanf("%d %d",&N, &M);
|
||||
int** dp = (int**)malloc(sizeof(int*)*(N+1));
|
||||
for(int i=0; i<=N; i++) dp[i] = (int*)malloc(sizeof(int)*(N+1));
|
||||
|
||||
for(int x=0; x<=N; x++) {
|
||||
for(int y=0; y<=N; y++) {
|
||||
if (x==0 || y==0) {
|
||||
dp[x][y] = 0;
|
||||
continue;
|
||||
}
|
||||
int num;
|
||||
scanf("%d",&num);
|
||||
dp[x][y] = dp[x-1][y] + dp[x][y-1] - dp[x-1][y-1] + num;
|
||||
}
|
||||
}
|
||||
|
||||
while(M--) {
|
||||
int x1, y1, x2, y2;
|
||||
scanf("%d %d %d %d", &x1, &y1, &x2, &y2);
|
||||
printf("%d\n",dp[x2][y2] - dp[x2][y1-1] - dp[x1-1][y2] + dp[x1-1][y1-1]);
|
||||
}
|
||||
|
||||
for(int i=0; i<=N; i++) free(dp[i]);
|
||||
free(dp);
|
||||
return 0;
|
||||
}
|
||||
97
code_study/Baekjoon/c/1167.c
Normal file
97
code_study/Baekjoon/c/1167.c
Normal file
@ -0,0 +1,97 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct Edge {
|
||||
int to;
|
||||
int weight;
|
||||
struct Edge* next;
|
||||
} edge;
|
||||
|
||||
typedef struct {
|
||||
int now;
|
||||
int distance;
|
||||
} tuple;
|
||||
|
||||
void add_edge(edge** graph, int u, int v, int w);
|
||||
tuple find_maxLength_node(int start, edge** graph, int v);
|
||||
|
||||
int main() {
|
||||
int V;
|
||||
scanf("%d",&V);
|
||||
|
||||
edge** graph = (edge**)malloc(sizeof(edge*)*(V+1));
|
||||
|
||||
for(int i=0; i<V; i++) {
|
||||
int u;
|
||||
scanf("%d",&u);
|
||||
graph[u] = NULL;
|
||||
|
||||
while(1) {
|
||||
int v;
|
||||
scanf("%d",&v);
|
||||
if(v==-1) break;
|
||||
|
||||
int w;
|
||||
scanf("%d",&w);
|
||||
|
||||
add_edge(graph,u,v,w);
|
||||
}
|
||||
}
|
||||
|
||||
int start = find_maxLength_node(1, graph, V).now;
|
||||
int radius = find_maxLength_node(start, graph, V).distance;
|
||||
printf("%d\n",radius);
|
||||
|
||||
|
||||
|
||||
for(int i = 1; i <= V; i++) {
|
||||
edge* ptr = graph[i];
|
||||
while(ptr != NULL) {
|
||||
edge* temp = ptr;
|
||||
ptr = ptr->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
free(graph);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void add_edge(edge** graph, int u, int v, int w) {
|
||||
edge* new_edge = (edge*)malloc(sizeof(edge));
|
||||
new_edge -> to = v;
|
||||
new_edge -> weight = w;
|
||||
new_edge -> next = graph[u];
|
||||
graph[u] = new_edge;
|
||||
}
|
||||
|
||||
tuple find_maxLength_node(int start, edge** graph, int v) {
|
||||
tuple result = {start, 0};
|
||||
bool* visited = (bool*)calloc(v+1,sizeof(bool));
|
||||
tuple* stack = (tuple*)malloc(sizeof(tuple)*v);
|
||||
int top = -1;
|
||||
|
||||
stack[++top] = result;
|
||||
visited[start] = true;
|
||||
|
||||
while(top != -1) {
|
||||
tuple current = stack[top--];
|
||||
|
||||
if(result.distance < current.distance) result = current;
|
||||
|
||||
edge* ptr = graph[current.now];
|
||||
while(ptr != NULL) {
|
||||
if(!visited[ptr->to]) {
|
||||
tuple next = {ptr->to, ptr->weight + current.distance};
|
||||
stack[++top] = next;
|
||||
visited[ptr->to] = true;
|
||||
}
|
||||
ptr = ptr -> next;
|
||||
}
|
||||
}
|
||||
|
||||
free(visited);
|
||||
free(stack);
|
||||
return result;
|
||||
}
|
||||
9
code_study/Baekjoon/c/11718.c
Normal file
9
code_study/Baekjoon/c/11718.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
char s;
|
||||
while(scanf("%c",&s)!= EOF){
|
||||
printf("%c",s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
12
code_study/Baekjoon/c/11720.c
Normal file
12
code_study/Baekjoon/c/11720.c
Normal file
@ -0,0 +1,12 @@
|
||||
#include <stdio.h>
|
||||
int main(){
|
||||
int n, sum=0;
|
||||
char s[101];
|
||||
scanf("%d",&n);
|
||||
scanf("%s",s);
|
||||
for(int i=0; i<n; i++){
|
||||
sum += (int)(s[i]-'0');
|
||||
}
|
||||
printf("%d",sum);
|
||||
return 0;
|
||||
}
|
||||
34
code_study/Baekjoon/c/11723.c
Normal file
34
code_study/Baekjoon/c/11723.c
Normal file
@ -0,0 +1,34 @@
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
int set = 0;
|
||||
int N;
|
||||
scanf("%d",&N);
|
||||
getchar();
|
||||
while(N--) {
|
||||
char str[20];
|
||||
if (fgets(str, sizeof(str), stdin) == NULL) break;
|
||||
if (str[0] == '\n') { N++; continue; }
|
||||
str[strcspn(str, "\n")] = 0;
|
||||
char *opcode = strtok(str, " ");
|
||||
char *operend = strtok(NULL, " ");
|
||||
|
||||
if(!strcmp(opcode, "add")) {
|
||||
set |= 1 << (atoi(operend)-1);
|
||||
} else if(!strcmp(opcode, "remove")) {
|
||||
set &= ~(1 << (atoi(operend)-1));
|
||||
} else if(!strcmp(opcode, "check")) {
|
||||
printf("%d\n",set & (1 << (atoi(operend)-1)) ? 1 : 0);
|
||||
} else if(!strcmp(opcode, "toggle")) {
|
||||
set ^= 1 << (atoi(operend)-1);
|
||||
} else if(!strcmp(opcode, "all")) {
|
||||
set = (1<<20) - 1;
|
||||
} else if(!strcmp(opcode, "empty")) {
|
||||
set = 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
35
code_study/Baekjoon/c/11866.c
Normal file
35
code_study/Baekjoon/c/11866.c
Normal file
@ -0,0 +1,35 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int nextIndex(int current_idx, int* arr, int n, int k);
|
||||
|
||||
int main() {
|
||||
int n, k;
|
||||
scanf("%d %d",&n, &k);
|
||||
int* list = (int*)malloc(sizeof(int)*(n+1));
|
||||
for(int i=0; i<=n; i++) {
|
||||
list[i] = i;
|
||||
}
|
||||
|
||||
printf("<");
|
||||
int len = n, current_idx=1;
|
||||
while(len!=0) {
|
||||
current_idx = nextIndex(current_idx, list, n, len==n ? k-1 : k);
|
||||
if(len==1) printf("%d>\n", list[current_idx]);
|
||||
else printf("%d, ",list[current_idx]);
|
||||
list[current_idx] = 0;
|
||||
len--;
|
||||
}
|
||||
free(list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nextIndex(int current_idx, int* arr, int n, int k){
|
||||
while(k--){
|
||||
do{
|
||||
if(current_idx==n) current_idx = 1;
|
||||
else current_idx++;
|
||||
}while(arr[current_idx]==0);
|
||||
}
|
||||
return current_idx;
|
||||
}
|
||||
26
code_study/Baekjoon/c/1193.c
Normal file
26
code_study/Baekjoon/c/1193.c
Normal file
@ -0,0 +1,26 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main(){
|
||||
int X;
|
||||
scanf("%d",&X);
|
||||
|
||||
int line=1, line_max=1, denum;
|
||||
do{
|
||||
if(X>line_max){
|
||||
line_max+=line+1;
|
||||
}
|
||||
else{
|
||||
if(line%2){
|
||||
denum = line-(line_max-X);
|
||||
}
|
||||
else{
|
||||
denum = line_max-X+1;
|
||||
}
|
||||
printf("%d/%d",line-denum+1, denum);
|
||||
break;
|
||||
}
|
||||
}while(line++);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
17
code_study/Baekjoon/c/11945.c
Normal file
17
code_study/Baekjoon/c/11945.c
Normal file
@ -0,0 +1,17 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main() {
|
||||
int N, M;
|
||||
scanf("%d %d", &N, &M);
|
||||
|
||||
char* line = (char*)malloc(sizeof(char)*(M+1));
|
||||
while(N--) {
|
||||
scanf("%s",line);
|
||||
for(int i=M-1; i>=0; i--) printf("%c",line[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
free(line);
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user