Skip to content

Commit

Permalink
Merge pull request #315 from WalletConnect/feature/chat-samplle-app-#298
Browse files Browse the repository at this point in the history
[Showcase] Chat sample app template
  • Loading branch information
flypaper0 authored Jul 7, 2022
2 parents 01c724b + a7618c7 commit bdd8d3e
Show file tree
Hide file tree
Showing 64 changed files with 1,906 additions and 0 deletions.
477 changes: 477 additions & 0 deletions Example/ExampleApp.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions Example/Showcase/Classes/ApplicationLayer/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {

}
}
8 changes: 8 additions & 0 deletions Example/Showcase/Classes/ApplicationLayer/Application.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

final class Application {

let chatService: ChatService = {
return ChatService()
}()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import UIKit

struct AppearanceConfigurator: Configurator {

func configure() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
struct ApplicationConfigurator: Configurator {

private let app: Application

init(app: Application) {
self.app = app
}

func configure() {
ChatListModule.create(app: app)
.wrapToNavigationController()
.present()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
protocol Configurator {
func configure()
}

extension Array where Element == Configurator {
func configure() {
forEach { $0.configure() }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
struct MigrationConfigurator: Configurator {

let app: Application

init(app: Application) {
self.app = app
}

func configure() {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
struct ThirdPartyConfigurator: Configurator {

func configure() {

}
}
26 changes: 26 additions & 0 deletions Example/Showcase/Classes/ApplicationLayer/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

private let app = Application()

private var configurators: [Configurator] {
return [
MigrationConfigurator(app: app),
ApplicationConfigurator(app: app),
AppearanceConfigurator(),
ThirdPartyConfigurator()
]
}

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }

window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()

configurators.configure()
}
}
39 changes: 39 additions & 0 deletions Example/Showcase/Classes/DomainLayer/ChatService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import Foundation
import Combine

typealias MessageStream = AsyncPublisher<AnyPublisher<[Message], Never>>

final class ChatService {

private let messagesSubject: CurrentValueSubject<[Message], Never> = {
return CurrentValueSubject([
Message(message: "✨gm", authorAccount: "2", timestamp: 0),
Message(message: "how r u man?", authorAccount: "1", timestamp: 0),
Message(message: "good", authorAccount: "2", timestamp: 0),
Message(message: "u?", authorAccount: "2", timestamp: 0),
Message(message: "I’m so happy I have you as my best friend, and I love Lisa so much", authorAccount: "1", timestamp: 0),
Message(message: "Why, Lisa, why, WHY?!", authorAccount: "2", timestamp: 0),
Message(message: "It’s bullshit, I did not hit her. I did nooot. Oh hi, Mark!", authorAccount: "1", timestamp: 0),
Message(message: "Johnny’s my best friend!", authorAccount: "2", timestamp: 0),
Message(message: "Anyway, how’s your sex life?", authorAccount: "1", timestamp: 0)
])
}()

func getMessages(topic: String) -> MessageStream {
return messagesSubject.eraseToAnyPublisher().values
}

func getAuthorAccount() async -> String {
return "1"
}

func sendMessage(text: String) async throws {
let authorAccount = await getAuthorAccount()
let message = Message(
message: text,
authorAccount: authorAccount,
timestamp: Int64(Date().timeIntervalSince1970)
)
messagesSubject.send(messagesSubject.value + [message])
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
final class ChatInteractor {

private let chatService: ChatService

init(chatService: ChatService) {
self.chatService = chatService
}

func getCurrentAccount() async -> String {
return await chatService.getAuthorAccount()
}

func getMessages(topic: String) -> MessageStream {
return chatService.getMessages(topic: topic)
}

func sendMessage(text: String) async throws {
try await chatService.sendMessage(text: text)
}
}
18 changes: 18 additions & 0 deletions Example/Showcase/Classes/PresentationLayer/Chat/ChatModule.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import SwiftUI

final class ChatModule {

@discardableResult
static func create(app: Application) -> UIViewController {
let router = ChatRouter(app: app)
let interactor = ChatInteractor(chatService: app.chatService)
let presenter = ChatPresenter(topic: "", interactor: interactor, router: router)
let view = ChatView().environmentObject(presenter)
let viewController = SceneViewController(viewModel: presenter, content: view)

router.viewController = viewController

return viewController
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import UIKit
import Combine

final class ChatPresenter: ObservableObject {

private let topic: String
private let interactor: ChatInteractor
private let router: ChatRouter
private var disposeBag = Set<AnyCancellable>()

@Published var messages: [MessageViewModel] = []
@Published var input: String = .empty

init(topic: String, interactor: ChatInteractor, router: ChatRouter) {
self.topic = topic
self.interactor = interactor
self.router = router
}

@MainActor
func setupInitialState() async {
let account = await interactor.getCurrentAccount()

for await messages in interactor.getMessages(topic: topic) {
self.messages = messages
.sorted(by: { $0.timestamp < $1.timestamp })
.map { MessageViewModel(message: $0, currentAccount: account) }
}
}

func didPressSend() {
sendMessage()
}
}

// MARK: SceneViewModel

extension ChatPresenter: SceneViewModel {

var sceneTitle: String? {
return "Chat"
}
}

// MARK: Privates

private extension ChatPresenter {

func sendMessage() {
Task {
try! await interactor.sendMessage(text: input)
input = .empty
}
}
}
12 changes: 12 additions & 0 deletions Example/Showcase/Classes/PresentationLayer/Chat/ChatRouter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import UIKit

final class ChatRouter {

weak var viewController: UIViewController!

private let app: Application

init(app: Application) {
self.app = app
}
}
41 changes: 41 additions & 0 deletions Example/Showcase/Classes/PresentationLayer/Chat/ChatView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import SwiftUI

struct ChatView: View {

@EnvironmentObject var presenter: ChatPresenter

var body: some View {
ZStack {
ChatScrollView {
// TODO: Replace id
ForEach(presenter.messages, id: \.text) { message in
MessageView(message: message)
}

Spacer().frame(height: 72)
}

VStack {
Spacer()

HStack {
InputView(title: "Message...", text: $presenter.input) {
presenter.didPressSend()
}
.padding(16.0)
}
}
}
.task {
await presenter.setupInitialState()
}
}
}

#if DEBUG
struct ChatView_Previews: PreviewProvider {
static var previews: some View {
ChatView()
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

// TODO: After Chat SDK integration
struct Message: Codable, Equatable {
let message: String
let authorAccount: String
let timestamp: Int64
}

struct MessageViewModel {
private let message: Message
private let currentAccount: String

init(message: Message, currentAccount: String) {
self.message = message
self.currentAccount = currentAccount
}

var isCurrentUser: Bool {
return currentAccount == message.authorAccount
}

var text: String {
return message.message
}

var showAvatar: Bool {
return !isCurrentUser
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import SwiftUI

struct ChatScrollView<Content>: View where Content: View {

@ViewBuilder let content: () -> Content

var body: some View {
ScrollView(showsIndicators: false) {
VStack(alignment: .leading, spacing: 12) {
Spacer()
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .topLeading
)

content()
}
.rotationEffect(Angle(degrees: 180))
}
.rotationEffect(Angle(degrees: 180))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import SwiftUI

struct ContentMessageView: View {

let text: String
let isCurrentUser: Bool

var body: some View {
Text(text)
.font(.body)
.padding(.horizontal, 16.0)
.padding(.vertical, 10.0)
.foregroundColor(.white)
.background(
// TODO: Add border
overlayView
.foregroundColor(backgroundColor)
)
}

private var overlayView: some View {
return Rectangle()
.cornerRadius(22, corners: [.topLeft, .topRight])
.cornerRadius(22, corners: isCurrentUser ? .bottomLeft : .bottomRight)
.cornerRadius(4, corners: isCurrentUser ? .bottomRight : .bottomLeft)
}

private var backgroundColor: Color {
return isCurrentUser ? .w_secondaryBackground : .w_purpleBackground
}

private var borderColor: Color {
return isCurrentUser ? .w_tertiaryBackground : .w_purpleForeground
}
}
Loading

0 comments on commit bdd8d3e

Please sign in to comment.