Skip to content

Commit

Permalink
Merge branch 'main' into CI/Release
Browse files Browse the repository at this point in the history
  • Loading branch information
ldakhoa committed Feb 4, 2023
2 parents 499d07d + a9d878a commit 4cd34c8
Show file tree
Hide file tree
Showing 120 changed files with 18,916 additions and 273 deletions.
8 changes: 6 additions & 2 deletions Documentation/RoadMap.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@
- [ ] Multi user

### Gist
- [ ] Create gists
- [x] Create gists
- [x] Search files in each gist
- [x] View gists
- [ ] Update gist
- [x] Content
- [x] Description
- [ ] Filename + Language
- [ ] Change filename + language
- [ ] Add files
- [ ] Add image
- [x] Delete file
- [x] Alert when content different than before
- [x] Share gists
Expand Down Expand Up @@ -44,8 +46,10 @@

### Misc
- [ ] Create GistHub issues in-app
- [ ] Open GistHub from safari
- [ ] Accessibility
- [x] Text size
- [ ] Reduce Motion
- [ ] Voice over
- [ ] View other user's gists
- [ ] Use new navigation stack
2 changes: 1 addition & 1 deletion GistHub.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
MARKETING_VERSION=1.0.3
MARKETING_VERSION=1.0.4
347 changes: 329 additions & 18 deletions GistHub.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@
"version" : "1.0.2"
}
},
{
"identity" : "highlightr",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ldakhoa/Highlightr",
"state" : {
"revision" : "ef5856b43b8a50e4292bb39b905c7cbf615c856f",
"version" : "1.0.2"
}
},
{
"identity" : "htmlstring",
"kind" : "remoteSourceControl",
"location" : "https://github.com/alexaubry/HTMLString",
"state" : {
"revision" : "04613a3b0f40a91e554b5174c0a10657f1a26357",
"version" : "6.0.1"
}
},
{
"identity" : "inject",
"kind" : "remoteSourceControl",
Expand Down Expand Up @@ -63,6 +81,15 @@
"version" : "0.2.10"
}
},
{
"identity" : "styledtextkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ldakhoa/StyledTextKit",
"state" : {
"revision" : "f4e75310e524797e8e750829e90c3620cb0f8dce",
"version" : "1.0.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
Expand Down
62 changes: 62 additions & 0 deletions GistHub/App/GitHubEmoji.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
//
// GitHubEmoji.swift
// GistHub
//
// Created by Khoa Le on 05/01/2023.
//

import Foundation

struct GitHubEmoji {
let emoji: String
let name: String
let aliases: [String]
let tags: [String]

init?(dict: [String: Any]) {
guard let emoji = dict["emoji"] as? String,
let aliases = dict["aliases"] as? [String],
let name = aliases.first,
let tags = dict["tags"] as? [String]
else { return nil }

self.emoji = emoji
self.name = name
self.aliases = aliases
self.tags = tags
}

}

// alias = ":smiley:" (surrounded w/ colons), search = words
typealias EmojiStore = (alias: [String: GitHubEmoji], search: [String: [GitHubEmoji]])

let GithubEmojis: EmojiStore = {
guard let url = Bundle.main.url(forResource: "emoji", withExtension: "json"),
let data = try? Data(contentsOf: url),
let json = try? JSONSerialization.jsonObject(with: data, options: .init(rawValue: 0)),
let dict = json as? [[String: Any]] else { return ([:], [:]) }

let emojis = dict.compactMap { GitHubEmoji(dict: $0) }

var aliasMap = [String: GitHubEmoji]()
var searchMap = [String: [GitHubEmoji]]()

for emoji in emojis {
for alias in emoji.aliases {
aliasMap[":" + alias + ":"] = emoji

// aliases have to be unique
searchMap[alias] = [emoji]
}

// collect all emoji tags
for tag in emoji.tags {
var arr = searchMap[tag] ?? []
arr.append(emoji)
searchMap[tag] = arr
}
}

return (aliasMap, searchMap)
}()
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct CommentView: View {
@State private var showPlainTextEditorView = false
@State private var showQuoteCommentTextEditor = false
@ObserveInjection private var inject
@State private var commentMarkdownHeight: CGFloat = 0

init(comment: Comment, gistID: String, viewModel: CommentViewModel) {
self.comment = comment
Expand All @@ -29,7 +30,7 @@ struct CommentView: View {

var body: some View {
VStack(alignment: .leading) {
HStack(alignment: .center) {
HStack(alignment: .bottom) {
if
let avatarURLString = comment.user.avatarURL,
let url = URL(string: avatarURLString)
Expand All @@ -38,10 +39,10 @@ struct CommentView: View {
.url(url)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)
.frame(width: 44, height: 44)
.cornerRadius(24)
}
VStack(alignment: .leading, spacing: 4) {
VStack(alignment: .leading, spacing: -6) {
HStack {
Text(comment.user.login ?? "")
.bold()
Expand Down Expand Up @@ -83,8 +84,13 @@ struct CommentView: View {
}
}
Spacer(minLength: 12)
Text(.init(comment.body ?? ""))
.font(.callout)

MarkdownUI(
markdown: comment.body ?? "",
markdownHeight: $commentMarkdownHeight,
mode: .comment)
.frame(height: commentMarkdownHeight)
.padding(.horizontal, -16)
}
.confirmationDialog("", isPresented: $showContentActionConfirmedDialog) {
if comment.user.id == userStore.user.id {
Expand Down Expand Up @@ -115,7 +121,7 @@ struct CommentView: View {
}
}
.sheet(isPresented: $showPlainTextEditorView) {
PlainTextEditorView(
MarkdownTextEditorView(
style: .updateComment,
content: comment.body ?? "",
gistID: gistID,
Expand All @@ -125,8 +131,8 @@ struct CommentView: View {
commentViewModel: viewModel)
}
.sheet(isPresented: $showQuoteCommentTextEditor) {
PlainTextEditorView(
style: .comment,
MarkdownTextEditorView(
style: .writeComment,
content: quoteBody(body: comment.body ?? ""),
gistID: gistID,
navigationTitle: "Write Comment",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct EditorDisplayView: View {
.sheet(isPresented: $showEditorInEditMode) {
NavigationView {
EditorView(
style: .update,
fileName: fileName,
content: content,
language: language,
Expand Down Expand Up @@ -113,21 +114,14 @@ struct EditorDisplayView: View {
self.completion()
dismiss()
}
.toast(isPresenting: $showErrorToast, duration: 2.5) {
AlertToast(
displayMode: .banner(.pop),
type: .error(Colors.danger.color),
title: error,
style: .style(backgroundColor: Colors.errorToastBackground.color)
)
}
.toastError(isPresenting: $showErrorToast, error: error)
.enableInjection()
}

func buildBodyView() -> some View {
Group {
if language == .markdown {
MarkdownPreviewView(markdown: content)
MarkdownUI(markdown: content)
} else {
EditorViewRepresentable(content: $content, language: language, isEditable: false)
}
Expand Down
145 changes: 145 additions & 0 deletions GistHub/Components/Editor/EditorView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
//
// EditorView.swift
// GistHub
//
// Created by Khoa Le on 14/12/2022.
//

import AlertToast
import SwiftUI
import Inject

struct EditorView: View {

// MARK: - Dependencies

private let style: Style
private let fileName: String
@State private var content: String
private let language: File.Language
private let gist: Gist?
private let navigationTitle: String
private let updateContentCompletion: (() -> Void)?
private let createGistCompletion: ((File) -> Void)?

// Only need if style is create
@State private var files: [String: File]?

// MARK: - State

@Environment(\.dismiss) private var dismiss
@ObserveInjection private var inject
@StateObject private var viewModel = EditorViewModel()
@State var originalContent: String = ""

@State private var contentHasChanged = false
@State private var showErrorToast = false
@State private var showConfirmDialog = false
@State private var error = ""

// MARK: - Initializer

init(
style: Style,
fileName: String,
content: String = "",
language: File.Language,
gist: Gist? = nil,
navigationTitle: String = "Edit",
files: [String: File]? = nil,
updateContentCompletion: (() -> Void)? = nil,
createGistCompletion: ((File) -> Void)? = nil
) {
self.style = style
self.fileName = fileName
_content = State(initialValue: content)
self.language = language
self.gist = gist
self.navigationTitle = navigationTitle
_files = State(wrappedValue: files)
self.updateContentCompletion = updateContentCompletion
self.createGistCompletion = createGistCompletion
}

var body: some View {
EditorViewRepresentable(content: $content, language: language, isEditable: true)
.navigationTitle(style == .update ? "Edit" : fileName)
.navigationBarTitleDisplayMode(.inline)
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
if contentHasChanged {
showConfirmDialog.toggle()
} else {
dismiss()
}
}
.foregroundColor(Colors.accent.color)
.confirmationDialog("Are you sure you want to cancel?", isPresented: $showConfirmDialog, titleVisibility: .visible) {
Button("Discard Changes", role: .destructive) {
self.content = originalContent
dismiss()
}

} message: {
Text("Your changes will be discarded.")
}
}
ToolbarItem(placement: .navigationBarTrailing) {
Button(style == .update ? "Update" : "Save") {
switch style {
case .createFile:
createGist()
case .update:
updateGist()
}
}
.bold()
.foregroundColor(contentHasChanged ? Colors.accent.color : Colors.accentDisabled.color)
.disabled(!contentHasChanged)
}
}
.toolbarBackground(.visible, for: .navigationBar)
.enableInjection()
.onChange(of: content) { newValue in
NotificationCenter.default.post(name: .editorTextViewTextDidChange, object: newValue)
contentHasChanged = newValue != originalContent ? true : false
}
.toastError(isPresenting: $showErrorToast, error: error)
.onAppear {
self.originalContent = self.content
}
.interactiveDismissDisabled(contentHasChanged)
}

private func updateGist() {
Task {
do {
try await viewModel.updateGist(gistID: gist?.id ?? "", fileName: fileName, content: self.content) {
self.dismiss()
self.updateContentCompletion!()
if language == .markdown {
NotificationCenter.default.post(name: .markdownPreviewShouldReload, object: content)
}
}
} catch let updateError {
error = updateError.localizedDescription
showErrorToast.toggle()
}
}
}

private func createGist() {
let file = File(filename: fileName, content: self.content)
dismiss()
createGistCompletion!(file)
}
}

extension EditorView {
enum Style {
case createFile
case update
}
}
File renamed without changes.
Loading

0 comments on commit 4cd34c8

Please sign in to comment.