-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
120 changed files
with
18,916 additions
and
273 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
MARKETING_VERSION=1.0.3 | ||
MARKETING_VERSION=1.0.4 |
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
}() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Oops, something went wrong.