Commit 4fa31f89 authored by wieseoli's avatar wieseoli
Browse files

Merge branch 'Thesis_hannes_merge_ready_versuch1' into 'dev'

Thesis Hannes Staging

See merge request !85
parents 48479856 749cf9ce
This diff is collapsed.
......@@ -24,7 +24,7 @@ import SwiftUI
// TODO: Mark displayname as from CNContact
class ContactHandler {
static var cartoons = false
static var cartoons = StudySettings.useAvatars
private let store = CNContactStore()
static let handler = ContactHandler()
......
......@@ -53,6 +53,11 @@ class StudySettings {
static var securityIndicator: SecurityIndicator = SecurityIndicator.load() as! SecurityIndicator
static var invitationsmode: Inviation = Inviation.load() as! Inviation
/// whether to show a normal inbox (false) or a (currently slightly buggy) categorized interface (true)
static var showCategorizedInterface = false //|| true
static var categorizedInterface_flat = false || true
static var useAvatars = true
public static var invitationEnabled: Bool {
get {
return false
......
......@@ -53,8 +53,9 @@ struct SimpleMailRowView <M: DisplayMail>: View {
}
}
/*
struct SimpleMailRow_Previews: PreviewProvider {
static var previews: some View {
SimpleMailRowView(mail: ProxyData.PlainMail)
}
}
}*/
//
// Category.swift
// enzevalos_iphone
//
// Created by hanneh00 on 18.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
typealias CategoryV = Category
///what a category has (and needs) for values
protocol Category {
var name : String {get}
var description : String {get}
var icon : Image {get}
var isBigger : Bool {get}
var isResizable : Bool {get}
var searchFields : [SearchFilter] {get}
var filter : MailFilter {get}
var andPredicate : NSPredicate {get}
func body(model : CategoryViewModel)->AnyView
}
///standart values for a category if not specified otherways
extension CategoryV {
var description : String {""}
var icon : Image {Image(systemName: "envelope.fill")}
var isBigger : Bool {false}
var isResizable : Bool {true}
var searchFields : [SearchFilter] {SearchFilterCollections.Standarts}
var andPredicate : NSPredicate {NSPredicate(value: true)}
}
///to which extend the category will open a specific mail
enum MailCategoryOpenability : Int16, Comparable{
static func < (lhs: MailCategoryOpenability, rhs: MailCategoryOpenability) -> Bool {
lhs.rawValue < rhs.rawValue
}
case cannotOpen = 0, ///: means this category wont open this email, wont display it and if forced to might cause undefined behavior
canOpen = 1, ///: the category can display this mail , but the category wont float to the top of the inbox
shouldOpen = 2, ///: the category displays this mails and floats to the top to show that it has new mail
mustOpen = 3 ///: overrides all other categories to force them to not open this mail (e.g. cause it is dangerous phishing)
}
typealias eMail = MailRecord
///each category needs a filter to return to which extend the category will open a specific mail
typealias MailFilter = (eMail)->MailCategoryOpenability
///what all the ViewModels use to compare to categories to see if they are the same (also the Database)
typealias CategoryIDType = String
//
// ChatCategory.swift
// enzevalos_iphone
//
// Created by hanneh00 on 23.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
let ChatCategory = CategoryView(
name : NSLocalizedString("Category.Chats.name", comment: "People"),
icon : Image(systemName: "message.fill"),
searchFields : SearchFilterCollections.Files,
filter : filterForShortMail //TODO: create an actually good filter
){model in
ChatCategoryView(model:model)
}
fileprivate func filterForShortMail(_ email : eMail)->MailCategoryOpenability{
let maxLength:Int = 200
guard email.body.count < maxLength else {
return .cannotOpen
}
return .shouldOpen
}
//
// ChatCategoryModel.swift
// enzevalos_iphone
//
// Created by hanneh00 on 26.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
///some extra functionality on top of the standort category functionality
///for the ChatCategory obv.
class ChatCategoryVM: ObservableObject {
init(_ categoryModel : CategoryViewModel) {
categoryBaseModel = categoryModel
}
@ObservedObject var categoryBaseModel : CategoryViewModel
var mails : [eMail] {
categoryBaseModel.allMails //group by sender?
}
var singleChats : [SingleChat] {
//would probably be cooler to fetch the users directly and filter their mails with an NSPredicate ?
var chatsByUser : [eMail.C : SingleChat] = [:]
for mail in mails {
if mail.addresses.count > 2 {continue} //group chat
guard let candidat1 = mail.addresses[safe: 0] else{continue}
guard let candidat2 = mail.addresses.last else{continue}
let _/*user*/ = !candidat1.isUser ? candidat1 : candidat2
let partner = !candidat1.isUser ? candidat2 : candidat1
if var alreadyCreatedChat = chatsByUser[partner]{
alreadyCreatedChat.messages.append(mail)
}else{
chatsByUser[partner]=SingleChat(
timeStamp: mail.date,
partner: partner,
messages: [mail]
)
}
}
//turn to array and sort by timeStamp
return chatsByUser.map{$1}.sorted(by: {$0.timeStamp >= $1.timeStamp})
}
//future work
var groupChatUsers : [GroupChat] {
//TODO
[]
}
/*
var isFullscreen : Bool {categoryBaseModel.size == nil}
func attachmentsize(_ attachmentCount:Int)->CGFloat {
return isFullscreen ? 250 : ((categoryBaseModel.size! - 50) / CGFloat(attachmentCount))
}
*/
}
struct SingleChat:Hashable{
var timeStamp : Date
var partner : eMail.C
var messages : [eMail]
}
//TODO future work
struct GroupChat{
var timeStamp : Date
var users : [eMail.C]
var messages : [eMail]
}
//
// ChatCategoryView.swift
// enzevalos_iphone
//
// Created by hanneh00 on 23.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
/// how the home of the chat category looks (with all the contacts in a list)
struct ChatCategoryView: View {
init(model : CategoryViewModel) {
baseModel = model
chatModel = ChatCategoryVM(model)
}
var baseModel : CategoryViewModel
@ObservedObject var chatModel : ChatCategoryVM
var body: some View {
VStack{
HStack{Spacer()}//make it use full width
if let size = baseModel.size {
let optimalAmount = min(Int(size)/70 , chatModel.singleChats.count)
//Preview
ForEach(0 ..< optimalAmount,id:\.self){i in if let chat = chatModel.singleChats[safe: i]{
Chat_PeopleRow(chat: chat)
}}
}else{
//FullScreen
ScrollView{
ForEach(chatModel.singleChats, id: \.self){chat in
Chat_PeopleRow(chat: chat)
}
}
}
}
.frame(height:baseModel.size).clipped()
}
}
//
// Chat_PeopleRow.swift
// enzevalos_iphone
//
// Created by hanneh00 on 25.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
import TagNavVi
struct Chat_PeopleRow: View {
let chat:SingleChat
@State var opens:Bool = false
var body : some View {
HStack{
profile
VStack {
nameView
Spacer().frame(height: 5)
HStack{
Text(chat.messages[0].body)
.font(.caption)
.lineLimit(2)
Spacer()
Text(chat.timeStamp.timeAgoText())
.font(.caption)
}
}.onTapGesture {
opens.toggle()
}
TagNavigationLink(parentTag: 1, destination: ChatView(chat:chat), isActive: $opens) {
EmptyView()
}
}
}
private var profile : some View {
chat.partner.avatar
.resizable()
.frame(width: 50, height: 50)
}
private var nameView : some View {
Text(chat.partner.name.split(separator: "@")[0])
.frame(maxWidth: 300, alignment: .leading)
.lineLimit(1)
.font(.subheadline)
}
}
struct Chat_PeopleRow_Previews: PreviewProvider {
static var previews: some View {
EmptyView()//Chat_PeopleRow()
}
}
//
// Bubble.swift
// enzevalos_iphone
//
// Created by hanneh00 on 23.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
///shows a single message in a chat (todo needs some refinement)
struct ChatMessageView : View {
var checkedChatMessage: eMail
var body: some View {
HStack(alignment: .bottom, spacing: 15) {
if !checkedChatMessage.sender.isUser{
checkedChatMessage.sender.avatar
.resizable()
.frame(width: 40, height: 40, alignment: .center)
.cornerRadius(20)
} else {
Spacer()
}
Bubble(contentChatMessage: checkedChatMessage.body, isCurrentChatUser: checkedChatMessage.sender.isUser)
.capsuledMailOptions(mail: checkedChatMessage)
}
}
}
struct Bubble: View {
var contentChatMessage: String
var isCurrentChatUser: Bool
var body: some View {
Text(contentChatMessage)
.padding(10)
.foregroundColor(isCurrentChatUser ? Color.white : Color.black)
.background(isCurrentChatUser ? Color.blue : Color(UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1.0)))
.cornerRadius(10)
}
}
struct Bubble_Previews: PreviewProvider {
static var previews: some View {
Bubble(contentChatMessage: "Hi, I am your friend", isCurrentChatUser: false)
}
}
//
// ChatView.swift
// enzevalos_iphone
//
// Created by hanneh00 on 23.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
///a complete chat with all its messages
struct ChatView: View {
@State var typingChatMessage: String = ""
var chat : SingleChat
var body: some View {
VStack {
ScrollView {
ForEach(chat.messages) { msg in
ChatMessageView(checkedChatMessage: msg)
}
}
HStack {
TextField("ChatMessage...", text: $typingChatMessage)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(minHeight: CGFloat(30))
Button(action: {/*TODO*/}) {
Text("Send")
}
}.frame(minHeight: CGFloat(50)).padding()
}
}
}
struct ChatView_Previews: PreviewProvider {
static var previews: some View {
EmptyView()//ChatView()
}
}
/*
small parts of the contents of this folder are cmd-c-cmd-v'd from https://www.iosapptemplates.com/blog/swiftui/swiftui-chat
*/
//
// FilesPreviewer.swift
// enzevalos_iphone
//
// Created by hanneh00 on 22.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
import TagNavVi
///shows all the files of each and every email
struct FilesPreviewer: View {
var height: CGFloat?
@ObservedObject var filesCategoryVM : FilesCategoryVM
@State var openEmail = false
var body: some View {
ScrollView(.horizontal,showsIndicators:false){HStack{ForEach(0..<filesCategoryVM.mailsAttachments.count){ mailIndex in
if let mailAttachments = filesCategoryVM.mailsAttachments[safe: mailIndex] {
if filesCategoryVM.isFullscreen {
ScrollView(.vertical, showsIndicators:false){attachmentColumn(filesCategoryVM.mails[mailIndex],attachments:mailAttachments)}
} else {
attachmentColumn(filesCategoryVM.mails[mailIndex],attachments:mailAttachments)
}
}
}}.padding(.horizontal)}
}
///the attachments of a single mail
@ViewBuilder func attachmentColumn(_ mail: eMail, attachments:[DisplayAttachment])->some View{
let height = filesCategoryVM.attachmentsize(attachments.count)
VStack{
Text(mail.subject)
.capsuledMailOptions(mail: mail)
ForEach(0..<attachments.count){ attachmentIndex in if let attachment = attachments[safe: attachmentIndex] {
AttPrev(
attachment: attachment
) //TODO: make this have the exact aspect ratio ?
.frame(width: 200).frame(height: height)
.padding(.horizontal, 40).padding(.bottom, -25).padding(.top, -7)
}}
}
}
}
struct FilesPreviewer_Previews: PreviewProvider {
static var previews: some View {
EmptyView()//FilesPreviewer()
}
}
//
// FilesCategory.swift
// enzevalos_iphone
//
// Created by hanneh00 on 21.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
let FilesCategory = CategoryView(
name : NSLocalizedString("Category.Files.name", comment: "Dateien"),
icon : Image(systemName: "archivebox.fill"),
searchFields : SearchFilterCollections.Files,
filter : filterForWithAttachment
){model in
FilesCategoryView(model:model)
}
fileprivate func filterForWithAttachment(_ email : eMail)->MailCategoryOpenability{
guard email.displayAttachments.count > 0 else {
return .cannotOpen
}
//TODO: filter for actually displayable attachments
return .canOpen
}
//
// FilesCategoryModel.swift
// enzevalos_iphone
//
// Created by hanneh00 on 22.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
class FilesCategoryVM: ObservableObject {
init(_ categoryModel : CategoryViewModel) {
categoryBaseModel = categoryModel
}
@ObservedObject var categoryBaseModel : CategoryViewModel
@Published var filteredFileTypes: [String] = []
func filterByTypes(_ email : eMail)->Bool{
filteredFileTypes.count == 0 ||
email.displayAttachments.map{filteredFileTypes.contains($0.myName.components(separatedBy: ".").last ?? "¿")}.contains(true)
}
var mails : [eMail] {
categoryBaseModel.allMails.filter(filterByTypes)
}
var mailsAttachments : [[DisplayAttachment]] {
mails.map{
$0.displayAttachments
.filter{attachment in
filteredFileTypes.count == 0 ||
filteredFileTypes.contains(attachment.myName.components(separatedBy: ".").last ?? "¿")
}
}
}
var isFullscreen : Bool {categoryBaseModel.size == nil}
func attachmentsize(_ attachmentCount:Int)->CGFloat {
return isFullscreen ? 250 : ((categoryBaseModel.size! - 50) / CGFloat(attachmentCount))
}
}
//
// FilesCategoryView.swift
// enzevalos_iphone
//
// Created by hanneh00 on 22.01.21.
// Copyright © 2021 fu-berlin. All rights reserved.
//
import SwiftUI
struct FilesCategoryView : View{
init(model : CategoryViewModel) {
baseModel = model
filesModel = FilesCategoryVM(model)
}
var baseModel : CategoryViewModel
@ObservedObject var filesModel : FilesCategoryVM
var body: some View{
VStack{
// TODO : there is nothing displayed for some reason currently
TypePicker(selectedTypes: $filesModel.filteredFileTypes).padding(.horizontal, -20).padding(.bottom , 5)
FilesPreviewer(height: baseModel.size, filesCategoryVM: filesModel).padding(.horizontal, -20)
}
}
}