Skip to content

Commit

Permalink
feat: adds grouping for chats by days (kevinhermawan#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinhermawan authored Nov 12, 2023
1 parent f03cdb4 commit 0502df6
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/kevinhermawan/ViewCondition",
"state" : {
"revision" : "f69a0ebf8cb6630c171a8bc284c6b9273590cc81",
"version" : "1.0.0"
"revision" : "3d6ff8a51f31890e091eac6872d966859d11ed76",
"version" : "1.0.1"
}
},
{
Expand Down
13 changes: 12 additions & 1 deletion Ollamac/ViewModels/ChatViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ final class ChatViewModel {
}

func fetch() throws {
let sortDescriptor = SortDescriptor(\Chat.createdAt, order: .reverse)
let sortDescriptor = SortDescriptor(\Chat.modifiedAt, order: .reverse)
let fetchDescriptor = FetchDescriptor<Chat>(sortBy: [sortDescriptor])

self.chats = try self.modelContext.fetch(fetchDescriptor)
Expand All @@ -46,4 +46,15 @@ final class ChatViewModel {

try self.modelContext.saveChanges()
}

func modify(_ chat: Chat) throws {
chat.modifiedAt = .now

if let index = self.chats.firstIndex(where: { $0.id == chat.id }) {
self.chats.remove(at: index)
self.chats.insert(chat, at: 0)
}

try self.modelContext.saveChanges()
}
}
68 changes: 62 additions & 6 deletions Ollamac/Views/ChatViews/ChatSidebarListView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,77 @@
//

import SwiftUI
import ViewCondition

struct ChatSidebarListView: View {
@Environment(CommandViewModel.self) private var commandViewModel
@Environment(ChatViewModel.self) private var chatViewModel

private var todayChats: [Chat] {
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())

return chatViewModel.chats.filter {
calendar.isDate($0.modifiedAt, inSameDayAs: today)
}
}

private var yesterdayChats: [Chat] {
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
let yesterday = calendar.date(byAdding: .day, value: -1, to: today)!

return chatViewModel.chats.filter {
calendar.isDate($0.modifiedAt, inSameDayAs: yesterday)
}
}

private var previousDays: [Chat] {
let calendar = Calendar.current
let today = calendar.startOfDay(for: Date())
let yesterday = calendar.date(byAdding: .day, value: -1, to: today)!

return chatViewModel.chats.filter {
!calendar.isDate($0.modifiedAt, inSameDayAs: today) && !calendar.isDate($0.modifiedAt, inSameDayAs: yesterday)
}
}

var body: some View {
@Bindable var commandViewModelBindable = commandViewModel

List(selection: $commandViewModelBindable.selectedChat) {
ForEach(chatViewModel.chats) { chat in
Label(chat.name, systemImage: "bubble")
.contextMenu {
ChatContextMenu(commandViewModel, for: chat)
}
.tag(chat)
Section(header: Text("Today")) {
ForEach(todayChats) { chat in
Label(chat.name, systemImage: "bubble")
.contextMenu {
ChatContextMenu(commandViewModel, for: chat)
}
.tag(chat)
}
}
.hide(if: todayChats.isEmpty, removeCompletely: true)

Section(header: Text("Yesterday")) {
ForEach(yesterdayChats) { chat in
Label(chat.name, systemImage: "bubble")
.contextMenu {
ChatContextMenu(commandViewModel, for: chat)
}
.tag(chat)
}
}
.hide(if: yesterdayChats.isEmpty, removeCompletely: true)

Section(header: Text("Previous Days")) {
ForEach(previousDays) { chat in
Label(chat.name, systemImage: "bubble")
.contextMenu {
ChatContextMenu(commandViewModel, for: chat)
}
.tag(chat)
}
}
.hide(if: previousDays.isEmpty, removeCompletely: true)
}
.listStyle(.sidebar)
.task {
Expand Down Expand Up @@ -67,6 +122,7 @@ struct ChatSidebarListView: View {
.dialogSeverity(.critical)
}

// MARK: - Actions
func deleteAction() {
guard let chatToDelete = commandViewModel.chatToDelete else { return }
try? chatViewModel.delete(chatToDelete)
Expand Down
94 changes: 49 additions & 45 deletions Ollamac/Views/MessageViews/MessageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
import OptionalKit
import SwiftUI
import SwiftUIIntrospect
import ViewCondition
import ViewState

struct MessageView: View {
private var chat: Chat

@Environment(\.modelContext) private var modelContext
@Environment(ChatViewModel.self) private var chatViewModel
@Environment(MessageViewModel.self) private var messageViewModel
@Environment(OllamaViewModel.self) private var ollamaViewModel

Expand Down Expand Up @@ -71,58 +73,58 @@ struct MessageView: View {
scrollToBottom(scrollViewProxy)
}

if !isEditorExpanded {
VStack(spacing: 8) {
HStack(alignment: .bottom, spacing: 16) {
PromptEditor(prompt: $prompt)
.frame(minHeight: 32, maxHeight: 256)
.fixedSize(horizontal: false, vertical: true)
.overlay(alignment: .topTrailing) {
Button(action: { isEditorExpanded = true }) {
Label("Expand", systemImage: "arrow.up.left.and.arrow.down.right")
.labelStyle(.iconOnly)
}
.padding(8)
.buttonStyle(.plain)
.keyboardShortcut("e", modifiers: .command)
.help("Expand editor (⌘ + E)")
VStack(spacing: 8) {
HStack(alignment: .bottom, spacing: 16) {
PromptEditor(prompt: $prompt)
.frame(minHeight: 32, maxHeight: 256)
.fixedSize(horizontal: false, vertical: true)
.overlay(alignment: .topTrailing) {
Button(action: { isEditorExpanded = true }) {
Label("Expand", systemImage: "arrow.up.left.and.arrow.down.right")
.labelStyle(.iconOnly)
}
.focused($isEditorFocused)
.onChange(of: messageViewModel.sendViewState) {
isEditorFocused = messageViewModel.sendViewState.isNil
}
.onChange(of: prompt) {
directSendAction()
}

Button(action: sendAction) {
Label("Send", systemImage: "paperplane")
.padding(8)
.foregroundStyle(.white)
.help("Send message (Return)")
.padding(8)
.buttonStyle(.plain)
.keyboardShortcut("e", modifiers: .command)
.help("Expand editor (⌘ + E)")
}
.buttonStyle(.borderedProminent)
.disabled(sendButtonDisabled)
}
.padding(.horizontal)

if let errorMessage {
HStack(alignment: .center) {
Text(errorMessage)
.focused($isEditorFocused)
.disabled(promptInputDisabled)
.onChange(of: messageViewModel.sendViewState) {
isEditorFocused = messageViewModel.sendViewState.isNil
}
.foregroundStyle(.red)
}

if messageViewModel.sendViewState == .error {
HStack(alignment: .center) {
Text(AppMessages.generalErrorMessage)
.onChange(of: prompt) {
directSendAction()
}
.foregroundStyle(.red)

Button(action: sendAction) {
Label("Send", systemImage: "paperplane")
.padding(8)
.foregroundStyle(.white)
.help("Send message (Return)")
}
.buttonStyle(.borderedProminent)
.disabled(sendButtonDisabled)
}
.padding(.horizontal)

if let errorMessage {
HStack(alignment: .center) {
Text(errorMessage)
}
.foregroundStyle(.red)
}

if messageViewModel.sendViewState == .error {
HStack(alignment: .center) {
Text(AppMessages.generalErrorMessage)
}
.foregroundStyle(.red)
}
.padding(.top, 8)
.padding(.bottom, 16)
}
.padding(.top, 8)
.padding(.bottom, 16)
.hide(if: isEditorExpanded, removeCompletely: true)
}
.navigationTitle(chat.name)
.navigationSubtitle(chat.model?.name ?? "")
Expand Down Expand Up @@ -150,6 +152,8 @@ struct MessageView: View {
}

private func sendAction() {
try? chatViewModel.modify(chat)

let message = Message(prompt: prompt, response: nil)
message.context = messageViewModel.messages.last?.context ?? []
message.chat = chat
Expand Down

0 comments on commit 0502df6

Please sign in to comment.