Skip to content

Commit c22eb75

Browse files
author
ziggzhang
committed
first commit for AIChat iOS project
1 parent 43483cd commit c22eb75

21 files changed

+1149
-0
lines changed

.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.DS_Store
2+
*.xcuserstate
3+
xcshareddata
4+
xcuserdata
5+

AIChat.xcodeproj/project.pbxproj

+613
Large diffs are not rendered by default.

AIChat.xcodeproj/project.xcworkspace/contents.xcworkspacedata

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

AIChat/AIChatApp.swift

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//
2+
// AIChatApp.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import SwiftUI
9+
10+
@main
11+
struct AIChatApp: App {
12+
var body: some Scene {
13+
WindowGroup {
14+
ChatListView()
15+
}
16+
}
17+
}

AIChat/AIGCDataManager.swift

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
//
2+
// AIGCDataManager.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import Foundation
9+
10+
class AIGCDataManager: ObservableObject {
11+
static let shared = AIGCDataManager()
12+
13+
@Published var chats: [Chat] = []
14+
15+
private let fileURL: URL
16+
17+
private init() {
18+
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
19+
self.fileURL = documentsPath.appendingPathComponent("chats.json")
20+
loadChats()
21+
}
22+
23+
func addChat(name: String, platform: AIPlatform, apiKey: String) {
24+
let newChat = Chat(id: UUID(), name: name, platform: platform, apiKey: apiKey, messages: [])
25+
chats.append(newChat)
26+
saveChats()
27+
}
28+
29+
func addMessage(to chatID: UUID, message: Message) {
30+
if let index = chats.firstIndex(where: { $0.id == chatID }) {
31+
chats[index].messages.append(message)
32+
saveChats()
33+
}
34+
}
35+
36+
private func saveChats() {
37+
do {
38+
let data = try JSONEncoder().encode(chats)
39+
try data.write(to: fileURL)
40+
} catch {
41+
print("Failed to save chats: \(error.localizedDescription)")
42+
}
43+
}
44+
45+
private func loadChats() {
46+
do {
47+
// Check if the file exists before attempting to read
48+
if FileManager.default.fileExists(atPath: fileURL.path) {
49+
let data = try Data(contentsOf: fileURL)
50+
51+
// Decode data if it's not empty
52+
if !data.isEmpty {
53+
chats = try JSONDecoder().decode([Chat].self, from: data)
54+
}
55+
} else {
56+
print("File does not exist. No data to load.")
57+
}
58+
} catch {
59+
print("Failed to load chats: \(error.localizedDescription)")
60+
// Optionally initialize with default or empty data
61+
chats = []
62+
}
63+
}
64+
}

AIChat/AISelectionView.swift

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//
2+
// AISelectionView.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import SwiftUI
9+
10+
struct AISelectionView: View {
11+
@ObservedObject var dataManager = AIGCDataManager.shared
12+
@Environment(\.presentationMode) var presentationMode
13+
14+
@State private var chatName: String = ""
15+
@State private var selectedPlatform: AIPlatform = .chatGPT // Default to a valid platform
16+
@State private var apiKey: String = ""
17+
@State private var customAPIURL: String = ""
18+
@State private var isCustomPlatform: Bool = false
19+
20+
var body: some View {
21+
ScrollView {
22+
VStack(alignment: .leading, spacing: 16) {
23+
TextField("Enter Chat Name", text: $chatName)
24+
.textFieldStyle(RoundedBorderTextFieldStyle())
25+
.padding(.horizontal)
26+
27+
Picker("Select AI Platform", selection: $selectedPlatform) {
28+
ForEach(AIPlatform.allCases) { platform in
29+
Text(platform.rawValue).tag(platform)
30+
}
31+
}
32+
.pickerStyle(MenuPickerStyle())
33+
.padding(.horizontal)
34+
.onChange(of: selectedPlatform) { newValue in
35+
// Update isCustomPlatform based on selected platform
36+
isCustomPlatform = (newValue == .custom)
37+
}
38+
39+
if isCustomPlatform {
40+
TextField("Enter Custom API URL", text: $customAPIURL)
41+
.textFieldStyle(RoundedBorderTextFieldStyle())
42+
.padding(.horizontal)
43+
}
44+
45+
TextField("Enter API Key", text: $apiKey)
46+
.textFieldStyle(RoundedBorderTextFieldStyle())
47+
.padding(.horizontal)
48+
.textInputAutocapitalization(.none) // Avoid auto-capitalization for API keys
49+
50+
Button("Save") {
51+
// Handle saving chat based on selection
52+
if selectedPlatform == .custom {
53+
// Handle custom API URL case
54+
print("Custom API URL: \(customAPIURL)")
55+
// Save or use custom API URL as needed
56+
} else {
57+
dataManager.addChat(name: chatName, platform: selectedPlatform, apiKey: apiKey)
58+
}
59+
presentationMode.wrappedValue.dismiss()
60+
}
61+
.padding(.horizontal)
62+
.padding(.bottom)
63+
}
64+
.padding(.top)
65+
}
66+
.navigationTitle("Select AI Platform")
67+
}
68+
}
69+
70+
struct AISelectionView_Previews: PreviewProvider {
71+
static var previews: some View {
72+
AISelectionView()
73+
}
74+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"colors" : [
3+
{
4+
"idiom" : "universal"
5+
}
6+
],
7+
"info" : {
8+
"author" : "xcode",
9+
"version" : 1
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"images" : [
3+
{
4+
"idiom" : "universal",
5+
"platform" : "ios",
6+
"size" : "1024x1024"
7+
}
8+
],
9+
"info" : {
10+
"author" : "xcode",
11+
"version" : 1
12+
}
13+
}

AIChat/Assets.xcassets/Contents.json

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}

AIChat/ChatListView.swift

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
//
2+
// ChatListView.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ChatListView: View {
11+
@ObservedObject var dataManager = AIGCDataManager.shared
12+
13+
var body: some View {
14+
NavigationView {
15+
List {
16+
ForEach(dataManager.chats) { chat in
17+
NavigationLink(destination: ChatView(chat: chat)) {
18+
VStack(alignment: .leading) {
19+
Text(chat.name)
20+
.font(.headline)
21+
if let lastMessage = chat.lastMessage {
22+
Text(lastMessage.content)
23+
.font(.subheadline)
24+
.foregroundColor(.gray)
25+
.lineLimit(1)
26+
Text(lastMessage.timestamp, style: .time)
27+
.font(.caption)
28+
.foregroundColor(.gray)
29+
} else {
30+
Text("No messages yet")
31+
.font(.subheadline)
32+
.foregroundColor(.gray)
33+
}
34+
}
35+
}
36+
}
37+
}
38+
.navigationTitle("Chats")
39+
.toolbar {
40+
ToolbarItem(placement: .navigationBarTrailing) {
41+
NavigationLink(destination: AISelectionView()) {
42+
Image(systemName: "plus")
43+
}
44+
}
45+
}
46+
}
47+
}
48+
}
49+
50+
struct ChatListView_Previews: PreviewProvider {
51+
static var previews: some View {
52+
ChatListView()
53+
}
54+
}

AIChat/ChatView.swift

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//
2+
// ChatView.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ChatView: View {
11+
@ObservedObject var dataManager = AIGCDataManager.shared
12+
let chat: Chat
13+
14+
@State private var inputText: String = ""
15+
16+
var body: some View {
17+
VStack {
18+
List {
19+
ForEach(chat.messages) { message in
20+
HStack {
21+
if message.isUser {
22+
Spacer()
23+
VStack(alignment: .trailing) {
24+
Text(message.content)
25+
.padding()
26+
.background(Color.blue)
27+
.cornerRadius(10)
28+
.foregroundColor(.white)
29+
Text(message.timestamp, style: .time)
30+
.font(.caption)
31+
.foregroundColor(.gray)
32+
}
33+
} else {
34+
VStack(alignment: .leading) {
35+
Text(message.content)
36+
.padding()
37+
.background(Color.gray.opacity(0.2))
38+
.cornerRadius(10)
39+
Text(message.timestamp, style: .time)
40+
.font(.caption)
41+
.foregroundColor(.gray)
42+
}
43+
Spacer()
44+
}
45+
}
46+
}
47+
}
48+
HStack {
49+
TextField("Enter message", text: $inputText)
50+
.textFieldStyle(RoundedBorderTextFieldStyle())
51+
Button("Send") {
52+
let userMessage = Message(id: UUID(), content: inputText, isUser: true, timestamp: Date())
53+
dataManager.addMessage(to: chat.id, message: userMessage)
54+
inputText = ""
55+
56+
// Simulate AI response (replace with actual API call)
57+
let aiResponse = Message(id: UUID(), content: "AI Response", isUser: false, timestamp: Date())
58+
dataManager.addMessage(to: chat.id, message: aiResponse)
59+
}
60+
.padding()
61+
}
62+
.padding()
63+
}
64+
.navigationTitle(chat.platform.rawValue)
65+
}
66+
}
67+
68+
struct ChatView_Previews: PreviewProvider {
69+
static var previews: some View {
70+
let sampleMessages = [
71+
Message(id: UUID(), content: "Hello!", isUser: true, timestamp: Date()),
72+
Message(id: UUID(), content: "Hi there!", isUser: false, timestamp: Date())
73+
]
74+
let chat = Chat(id: UUID(), name: "Sample Chat", platform: .chatGPT, apiKey: "YourAPIKeyHere", messages: sampleMessages)
75+
ChatView(chat: chat)
76+
}
77+
}
78+

AIChat/ContentView.swift

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//
2+
// ContentView.swift
3+
// AIChat
4+
//
5+
// Created by ziggzhang on 2024/7/21.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ContentView: View {
11+
var body: some View {
12+
VStack {
13+
Image(systemName: "globe")
14+
.imageScale(.large)
15+
.foregroundStyle(.tint)
16+
Text("Hello, world!")
17+
}
18+
.padding()
19+
}
20+
}
21+
22+
#Preview {
23+
ContentView()
24+
}

0 commit comments

Comments
 (0)