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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 184 additions & 11 deletions Whishpermate/WhisperMateIOS/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ struct ContentView: View {
@StateObject private var dictionaryManager = DictionaryManager.shared
@StateObject private var toneStyleManager = ToneStyleManager.shared
@StateObject private var shortcutManager = ShortcutManager.shared
@StateObject private var languageManager = LanguageManager()
@StateObject private var parakeetService = ParakeetTranscriptionService.shared
@EnvironmentObject var authManager: AuthManager
@EnvironmentObject var subscriptionManager: SubscriptionManager
@AppStorage("useOnDeviceTranscription", store: AppDefaults.shared) private var useOnDeviceTranscription = false
@State private var selectedTab: Int = 0
@State private var showRecordingSheet = false
@State private var selectedRecording: Recording?
Expand Down Expand Up @@ -42,10 +47,10 @@ struct ContentView: View {
}
.navigationTitle("WhisperMate")
.sheet(isPresented: $showRecordingSheet) {
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager)
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, languageManager: languageManager)
}
.sheet(item: $selectedRecording) { recording in
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, recording: recording)
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, languageManager: languageManager, recording: recording)
}
}
.navigationViewStyle(.stack)
Expand Down Expand Up @@ -110,7 +115,7 @@ struct ContentView: View {
}
}
.sheet(isPresented: $showRecordingSheet) {
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager)
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, languageManager: languageManager)
}
}

Expand Down Expand Up @@ -378,7 +383,7 @@ struct ContentView: View {
}
.navigationTitle("History")
.sheet(item: $selectedRecording) { recording in
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, recording: recording)
RecordingSheetView(historyManager: historyManager, dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager, languageManager: languageManager, recording: recording)
}
}
.navigationViewStyle(StackNavigationViewStyle())
Expand All @@ -389,6 +394,158 @@ struct ContentView: View {
private var settingsView: some View {
NavigationView {
Form {
// Account Section
Section("Account") {
if authManager.isAuthenticated, let user = authManager.currentUser {
HStack {
Image(systemName: "person.circle.fill")
.font(.title2)
.foregroundColor(.blue)
VStack(alignment: .leading, spacing: 2) {
Text(user.email)
.font(.body)
Text(user.subscriptionTier.displayName)
.font(.caption)
.foregroundColor(.secondary)
}
}

// Usage info
let usage = subscriptionManager.getUsageStatus()
if !usage.isPro {
VStack(alignment: .leading, spacing: 8) {
HStack {
Text("Words used")
.font(.subheadline)
Spacer()
Text("\(usage.used) / \(usage.limit)")
.font(.subheadline)
.foregroundColor(.secondary)
}
ProgressView(value: min(usage.percentage, 1.0))
.tint(usage.percentage >= 0.9 ? .red : .blue)
}

Button(action: {
subscriptionManager.openUpgrade()
}) {
Label("Upgrade to Pro", systemImage: "star.fill")
.foregroundColor(.orange)
}
}

Button("Sign Out", role: .destructive) {
Task {
await authManager.logout()
}
}
} else {
Button(action: {
authManager.openSignUp()
}) {
HStack {
Image(systemName: "person.circle")
.font(.title2)
.foregroundColor(.blue)
VStack(alignment: .leading, spacing: 2) {
Text("Sign In / Create Account")
.font(.body)
Text("Get \(UsageLimits.freeMonthlyWordLimit) free words/month")
.font(.caption)
.foregroundColor(.secondary)
}
}
}
}
}

// Language Section
Section("Language") {
NavigationLink(destination: LanguageSelectionView(languageManager: languageManager)) {
HStack {
Label("Transcription Language", systemImage: "globe")
Spacer()
Text(languageDisplayText)
.foregroundColor(.secondary)
.lineLimit(1)
}
}
}

// Transcription Engine Section
Section {
Toggle(isOn: $useOnDeviceTranscription) {
Label("On-Device (Parakeet)", systemImage: "iphone")
}
.onChange(of: useOnDeviceTranscription) { enabled in
if enabled {
Task {
try? await parakeetService.initialize()
}
}
}

switch parakeetService.state {
case .notInitialized:
if useOnDeviceTranscription {
HStack {
Text("Model not downloaded")
.font(.caption)
.foregroundColor(.secondary)
Spacer()
Button("Download") {
Task {
try? await parakeetService.initialize()
}
}
.font(.caption)
}
}
case .downloading:
HStack {
ProgressView()
.controlSize(.small)
Text("Downloading model...")
.font(.caption)
.foregroundColor(.secondary)
}
case .initializing:
HStack {
ProgressView()
.controlSize(.small)
Text("Initializing...")
.font(.caption)
.foregroundColor(.secondary)
}
case .ready:
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundColor(.green)
Text("Model ready")
.font(.caption)
.foregroundColor(.green)
}
case .transcribing:
HStack {
ProgressView()
.controlSize(.small)
Text("Transcribing...")
.font(.caption)
.foregroundColor(.secondary)
}
case .error(let message):
Text(message)
.font(.caption)
.foregroundColor(.red)
}
} header: {
Text("Transcription Engine")
} footer: {
Text(useOnDeviceTranscription
? "Transcription runs privately on your device. No internet required."
: "Transcription uses cloud API. Requires internet connection.")
}

Section("Permissions") {
Button(action: openAppSettings) {
HStack {
Expand All @@ -409,21 +566,21 @@ struct ContentView: View {
}
}

Section("Transcription") {
NavigationLink(destination: TranscriptionSettingsView(dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager)) {
Label("Dictionary & Shortcuts", systemImage: "text.badge.checkmark")
}
}

Section("About") {
HStack {
Text("Version")
Spacer()
Text("0.0.20")
Text(appVersion)
.foregroundColor(.secondary)
}
}

Section("Transcription") {
NavigationLink(destination: TranscriptionSettingsView(dictionaryManager: dictionaryManager, toneStyleManager: toneStyleManager, shortcutManager: shortcutManager)) {
Label("Transcription Settings", systemImage: "text.badge.checkmark")
}
}

Section("Data") {
Button("Clear All History", role: .destructive) {
historyManager.clearAll()
Expand All @@ -436,6 +593,20 @@ struct ContentView: View {
.navigationViewStyle(StackNavigationViewStyle())
}

// MARK: - Computed Properties

private var appVersion: String {
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "0.0.1"
}

private var languageDisplayText: String {
if languageManager.selectedLanguages.contains(.auto) {
return "Auto-detect"
}
let names = languageManager.selectedLanguages.map { $0.displayName }
return names.sorted().joined(separator: ", ")
}

// MARK: - Permission Helpers

private func checkMicrophonePermission() -> PermissionStatus {
Expand Down Expand Up @@ -561,4 +732,6 @@ enum PermissionStatus {

#Preview {
ContentView()
.environmentObject(AuthManager.shared)
.environmentObject(SubscriptionManager.shared)
}
36 changes: 36 additions & 0 deletions Whishpermate/WhisperMateIOS/LanguageSelectionView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import SwiftUI
import WhisperMateShared

struct LanguageSelectionView: View {
@ObservedObject var languageManager: LanguageManager

var body: some View {
List {
Section(footer: Text("Select the languages you speak. Auto-detect works best for single-language dictation.")) {
ForEach(Language.allCases) { language in
Button(action: {
languageManager.toggleLanguage(language)
}) {
HStack {
Text(language.flag)
.font(.title3)

Text(language.displayName)
.foregroundColor(.primary)

Spacer()

if languageManager.isSelected(language) {
Image(systemName: "checkmark")
.foregroundColor(.blue)
.fontWeight(.semibold)
}
}
}
}
}
}
.navigationTitle("Language")
.navigationBarTitleDisplayMode(.inline)
}
}
Loading