diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..3c02075 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,263 @@ +# If true, SwiftLint will not fail if no lintable files are found. +allow_zero_lintable_files: true +# If true, SwiftLint will treat all warnings as errors. +strict: false +reporter: "xcode" # reporter type (xcode, json, csv, checkstyle, codeclimate, junit, html, emoji, sonarqube, markdown, github-actions-logging, summary) +blanket_disable_command: + severity: warning + allowed_rules: ["file_header", "file_length", "file_name", "file_name_no_space", "single_test_class"] + always_blanket_disable: [] +block_based_kvo: + severity: warning +class_delegate_protocol: + severity: warning +closing_brace: + severity: warning +closure_parameter_position: + severity: warning +colon: + severity: warning + flexible_right_spacing: false + apply_to_dictionaries: true +comma: + severity: warning +comment_spacing: + severity: warning +compiler_protocol_init: + severity: warning +computed_accessors_order: + severity: warning + order: get_set +control_statement: + severity: warning +cyclomatic_complexity: + warning: 10 + error: 20 + ignores_case_statements: false +deployment_target: + severity: warning + iOSApplicationExtension_deployment_target: 7.0 + iOS_deployment_target: 7.0 + macOSApplicationExtension_deployment_target: 10.9 + macOS_deployment_target: 10.9 + tvOSApplicationExtension_deployment_target: 9.0 + tvOS_deployment_target: 9.0 + watchOSApplicationExtension_deployment_target: 1.0 + watchOS_deployment_target: 1.0 +discouraged_direct_init: + severity: warning + types: ["Bundle", "Bundle.init", "Bundle.init.init", "NSError", "NSError.init", "NSError.init.init", "UIDevice", "UIDevice.init", "UIDevice.init.init"] +duplicate_conditions: + severity: error +duplicate_enum_cases: + severity: error +duplicate_imports: + severity: warning +duplicated_key_in_dictionary_literal: + severity: warning +dynamic_inline: + severity: error +empty_enum_arguments: + severity: warning +empty_parameters: + severity: warning +empty_parentheses_with_trailing_closure: + severity: warning +file_length: + warning: 400 + error: 1000 + ignore_comment_only_lines: false +for_where: + severity: warning + allow_for_as_filter: false +force_cast: + severity: warning +force_try: + severity: error +function_body_length: + warning: 50 + error: 100 +function_parameter_count: + warning: 5 + error: 8 + ignores_default_parameters: true +generic_type_name: + min_length: + warning: 1 + error: 0 + max_length: + warning: 20 + error: 1000 + excluded: [] + allowed_symbols: [] + unallowed_symbols_severity: error + validates_start_with_lowercase: error +identifier_name: + min_length: + warning: 3 + error: 2 + max_length: + warning: 40 + error: 60 + excluded: ["^^id$$"] + allowed_symbols: [] + unallowed_symbols_severity: error + validates_start_with_lowercase: error +implicit_getter: + severity: warning +inclusive_language: + severity: warning +invalid_swiftlint_command: + severity: warning +is_disjoint: + severity: warning +large_tuple: + warning: 2 + error: 3 +leading_whitespace: + severity: warning +legacy_cggeometry_functions: + severity: warning +legacy_constant: + severity: warning +legacy_constructor: + severity: warning +legacy_hashing: + severity: warning +legacy_nsgeometry_functions: + severity: warning +legacy_random: + severity: warning +line_length: + warning: 120 + error: 200 + ignores_urls: false + ignores_function_declarations: false + ignores_comments: false + ignores_interpolated_strings: false +mark: + severity: warning +multiple_closures_with_trailing_closure: + severity: warning +nesting: + type_level: + warning: 1 + function_level: + warning: 2 + check_nesting_in_closures_and_statements: true + always_allow_one_type_in_functions: false +no_fallthrough_only: + severity: warning +no_space_in_method_call: + severity: warning +notification_center_detachment: + severity: warning +ns_number_init_as_function_reference: + severity: warning +nsobject_prefer_isequal: + severity: warning +opening_brace: + severity: warning + allow_multiline_func: false +operator_whitespace: + severity: warning +orphaned_doc_comment: + severity: warning +private_over_fileprivate: + severity: warning + validate_extensions: false +private_unit_test: + severity: warning + test_parent_classes: ["QuickSpec", "XCTestCase"] +protocol_property_accessors_order: + severity: warning +reduce_boolean: + severity: warning +redundant_discardable_let: + severity: warning +redundant_objc_attribute: + severity: warning +redundant_optional_initialization: + severity: warning +redundant_set_access_control: + severity: warning +redundant_string_enum_value: + severity: warning +redundant_void_return: + severity: warning +return_arrow_whitespace: + severity: warning +self_in_property_initialization: + severity: warning +shorthand_operator: + severity: error +statement_position: + severity: warning + statement_mode: uncuddled_else +superfluous_disable_command: + severity: warning +switch_case_alignment: + severity: warning + indented_cases: false +syntactic_sugar: + severity: warning +todo: + severity: warning +trailing_comma: + severity: warning + mandatory_comma: true +trailing_newline: + severity: warning +trailing_semicolon: + severity: warning +trailing_whitespace: + severity: warning + ignores_empty_lines: false + ignores_comments: true +type_body_length: + warning: 250 + error: 350 +type_name: + min_length: + warning: 3 + error: 0 + max_length: + warning: 40 + error: 1000 + excluded: [] + allowed_symbols: [] + unallowed_symbols_severity: error + validates_start_with_lowercase: error + validate_protocols: true +unavailable_condition: + severity: warning +unneeded_break_in_switch: + severity: warning +unneeded_override: + severity: warning +unneeded_synthesized_initializer: + severity: warning +unused_closure_parameter: + severity: warning +unused_control_flow_label: + severity: warning +unused_enumerated: + severity: warning +unused_optional_binding: + severity: warning + ignore_optional_try: false +unused_setter_value: + severity: warning +valid_ibinspectable: + severity: warning +vertical_parameter_alignment: + severity: warning +vertical_whitespace: + severity: warning + max_empty_lines: 2 +void_function_in_ternary: + severity: warning +void_return: + severity: warning +xctfail_message: + severity: warning diff --git a/Transcopied.xcodeproj/project.pbxproj b/Transcopied.xcodeproj/project.pbxproj index b00ef98..2213f57 100644 --- a/Transcopied.xcodeproj/project.pbxproj +++ b/Transcopied.xcodeproj/project.pbxproj @@ -7,20 +7,25 @@ objects = { /* Begin PBXBuildFile section */ + 1D4BCFD72B1AD79C00E10186 /* AppDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D4BCFD62B1AD79C00E10186 /* AppDetails.swift */; }; 1D8346862B1415AB004ACF46 /* CopiedItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D8346852B1415AB004ACF46 /* CopiedItem.swift */; }; + 1DBC765B2B1F5FA6004B1261 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1DFE67922B13168E000E36DA /* Assets.xcassets */; }; + 1DD331C92B19846400708F46 /* ModalMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DD331C82B19846400708F46 /* ModalMessage.swift */; }; 1DF36DD72B16EDC80037FA6A /* CopiedEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF36DD62B16EDC80037FA6A /* CopiedEditorView.swift */; }; - 1DF36DDB2B1823740037FA6A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1DFE67922B13168E000E36DA /* Assets.xcassets */; }; 1DFE678D2B13168D000E36DA /* Transcopied.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE678C2B13168D000E36DA /* Transcopied.swift */; }; 1DFE678F2B13168D000E36DA /* CopiedItemList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFE678E2B13168D000E36DA /* CopiedItemList.swift */; }; 1DFE67962B13168E000E36DA /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1DFE67952B13168E000E36DA /* Preview Assets.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 1D4BCFD62B1AD79C00E10186 /* AppDetails.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetails.swift; sourceTree = ""; }; 1D8346852B1415AB004ACF46 /* CopiedItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopiedItem.swift; sourceTree = ""; }; 1DD331BB2B1828CE00708F46 /* SwiftData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftData.framework; path = System/Library/Frameworks/SwiftData.framework; sourceTree = SDKROOT; }; 1DD331BC2B1828CE00708F46 /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = System/Library/Frameworks/CloudKit.framework; sourceTree = SDKROOT; }; 1DD331BD2B1828CE00708F46 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 1DD331BE2B1828CE00708F46 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 1DD331C62B19670A00708F46 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 1DD331C82B19846400708F46 /* ModalMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModalMessage.swift; sourceTree = ""; }; 1DF36DD62B16EDC80037FA6A /* CopiedEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopiedEditorView.swift; sourceTree = ""; }; 1DF36DD82B17881E0037FA6A /* Transcopied-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Transcopied-Info.plist"; sourceTree = ""; }; 1DF36DD92B17899F0037FA6A /* TranscopiedRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = TranscopiedRelease.entitlements; sourceTree = ""; }; @@ -46,6 +51,7 @@ 1DD331BA2B1828CE00708F46 /* Frameworks */ = { isa = PBXGroup; children = ( + 1DD331C62B19670A00708F46 /* .swiftlint.yml */, 1DD331BC2B1828CE00708F46 /* CloudKit.framework */, 1DD331BD2B1828CE00708F46 /* CoreFoundation.framework */, 1DD331BB2B1828CE00708F46 /* SwiftData.framework */, @@ -65,6 +71,7 @@ 1DD331BA2B1828CE00708F46 /* Frameworks */, ); sourceTree = ""; + usesTabs = 0; }; 1DFE678A2B13168D000E36DA /* Products */ = { isa = PBXGroup; @@ -83,6 +90,8 @@ 1DFE67922B13168E000E36DA /* Assets.xcassets */, 1DFE67942B13168E000E36DA /* Preview Content */, 1D8346852B1415AB004ACF46 /* CopiedItem.swift */, + 1DD331C82B19846400708F46 /* ModalMessage.swift */, + 1D4BCFD62B1AD79C00E10186 /* AppDetails.swift */, ); path = transcopied; sourceTree = ""; @@ -102,6 +111,7 @@ isa = PBXNativeTarget; buildConfigurationList = 1DFE67992B13168E000E36DA /* Build configuration list for PBXNativeTarget "Transcopied" */; buildPhases = ( + 1DD331C52B194D4F00708F46 /* ShellScript */, 1DFE67852B13168D000E36DA /* Sources */, 1DFE67862B13168D000E36DA /* Frameworks */, 1DFE67872B13168D000E36DA /* Resources */, @@ -153,13 +163,34 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1DF36DDB2B1823740037FA6A /* Assets.xcassets in Resources */, + 1DBC765B2B1F5FA6004B1261 /* Assets.xcassets in Resources */, 1DFE67962B13168E000E36DA /* Preview Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 1DD331C52B194D4F00708F46 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which swiftlint >/dev/null; then\n swiftlint;\nelse\n echo \"Warning swiftlint not installed! See https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ 1DFE67852B13168D000E36DA /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -167,6 +198,8 @@ files = ( 1DFE678F2B13168D000E36DA /* CopiedItemList.swift in Sources */, 1DFE678D2B13168D000E36DA /* Transcopied.swift in Sources */, + 1D4BCFD72B1AD79C00E10186 /* AppDetails.swift in Sources */, + 1DD331C92B19846400708F46 /* ModalMessage.swift in Sources */, 1DF36DD72B16EDC80037FA6A /* CopiedEditorView.swift in Sources */, 1D8346862B1415AB004ACF46 /* CopiedItem.swift in Sources */, ); @@ -179,7 +212,11 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "\"AppIcon Dark Alt\" \"AppIcon Light Alt\" \"AppIcon Dark\""; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon Light"; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -213,7 +250,7 @@ DEVELOPMENT_TEAM = V6MX2ZRT2L; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -228,7 +265,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - GENERATE_INFOPLIST_FILE = NO; + GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -245,7 +282,11 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "\"AppIcon Dark Alt\" \"AppIcon Light Alt\" \"AppIcon Dark\""; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon Light"; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -279,7 +320,7 @@ DEVELOPMENT_TEAM = V6MX2ZRT2L; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_USER_SCRIPT_SANDBOXING = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -288,7 +329,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - GENERATE_INFOPLIST_FILE = NO; + GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; @@ -303,8 +344,8 @@ 1DFE679A2B13168E000E36DA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = AppIconDark; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIconLight; + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon Light"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = TranscopiedDebug.entitlements; @@ -341,8 +382,8 @@ 1DFE679B2B13168E000E36DA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = AppIconDark; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIconLight; + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; + ASSETCATALOG_COMPILER_APPICON_NAME = "AppIcon Light"; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = TranscopiedRelease.entitlements; diff --git a/Transcopied.xcodeproj/xcshareddata/xcschemes/Test.xcscheme b/Transcopied.xcodeproj/xcshareddata/xcschemes/Test.xcscheme new file mode 100644 index 0000000..55ddc8e --- /dev/null +++ b/Transcopied.xcodeproj/xcshareddata/xcschemes/Test.xcscheme @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/transcopied/AppDetails.swift b/transcopied/AppDetails.swift new file mode 100644 index 0000000..edca5e5 --- /dev/null +++ b/transcopied/AppDetails.swift @@ -0,0 +1,53 @@ +// +// AppDetails.swift +// Transcopied +// +// Created by Dakota Lorance on 12/1/23. +// + +import SwiftUI + +struct AppDetails: View { + @AppStorage("support") private var support: URL = URL(string: "mailto:transcopied@dwl.dev")! + @AppStorage("project") private var project: URL = URL(string: "https://github.com/slyboots/transcopied")! + @AppStorage("privacy") private var privacy: URL = URL(string: "https://transcopied.dwl.dev/privacy")! + + var body: some View { + List { + Section { + Link(destination: support) { + Text("Email Support") + } + Link(destination: project) { + Text("Source Code") + } + Link(destination: privacy) { + Text("Privacy Policy") + } + } header: { + Text("About Transcopied") + .foregroundStyle(.accent) + .font(.subheadline) + } + Section { + Label { + HStack { + Text("❤️ Design/Icon:") + Text("Michelle Lorance") + }.font(.body) + } icon: {} + } header: { + Text("Credits") + .foregroundStyle(.accent) + .font(.subheadline) + } + } + .padding() + .navigationTitle("Settings") + .foregroundStyle(.primary) + } +} + +#Preview { + AppDetails() +} diff --git a/transcopied/Assets.xcassets/AccentColor.colorset/Contents.json b/transcopied/Assets.xcassets/AccentColor.colorset/Contents.json index a29102c..80979bd 100644 --- a/transcopied/Assets.xcassets/AccentColor.colorset/Contents.json +++ b/transcopied/Assets.xcassets/AccentColor.colorset/Contents.json @@ -5,9 +5,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.282", + "green" : "0.722", + "red" : "0.318" } }, "idiom" : "universal" @@ -23,9 +23,9 @@ "color-space" : "display-p3", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.282", + "green" : "0.722", + "red" : "0.318" } }, "idiom" : "universal" @@ -41,9 +41,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.718", + "green" : "0.278", + "red" : "0.682" } }, "idiom" : "universal" @@ -53,9 +53,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.282", + "green" : "0.722", + "red" : "0.318" } }, "idiom" : "universal", @@ -72,9 +72,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.282", + "green" : "0.722", + "red" : "0.318" } }, "idiom" : "universal", @@ -91,9 +91,9 @@ "color-space" : "srgb", "components" : { "alpha" : "1.000", - "blue" : "0.294", - "green" : "0.765", - "red" : "0.545" + "blue" : "0.718", + "green" : "0.278", + "red" : "0.682" } }, "idiom" : "universal", diff --git a/transcopied/Assets.xcassets/AppIcon Dark.appiconset/AppIcon~ios-marketing-dark.png b/transcopied/Assets.xcassets/AppIcon Dark.appiconset/AppIcon~ios-marketing-dark.png deleted file mode 100644 index 46a64c2..0000000 Binary files a/transcopied/Assets.xcassets/AppIcon Dark.appiconset/AppIcon~ios-marketing-dark.png and /dev/null differ diff --git a/transcopied/Assets.xcassets/AppIcon Dark.appiconset/Contents.json b/transcopied/Assets.xcassets/AppIcon Light.appiconset/Contents.json similarity index 76% rename from transcopied/Assets.xcassets/AppIcon Dark.appiconset/Contents.json rename to transcopied/Assets.xcassets/AppIcon Light.appiconset/Contents.json index be68e5e..101d086 100644 --- a/transcopied/Assets.xcassets/AppIcon Dark.appiconset/Contents.json +++ b/transcopied/Assets.xcassets/AppIcon Light.appiconset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "AppIcon~ios-marketing-dark.png", + "filename" : "Trans Copy Logo Light-1024.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/transcopied/Assets.xcassets/AppIcon Light.appiconset/Trans Copy Logo Light-1024.png b/transcopied/Assets.xcassets/AppIcon Light.appiconset/Trans Copy Logo Light-1024.png new file mode 100644 index 0000000..e6f1fcd Binary files /dev/null and b/transcopied/Assets.xcassets/AppIcon Light.appiconset/Trans Copy Logo Light-1024.png differ diff --git a/transcopied/Assets.xcassets/AppIconLight.appiconset/AppIcon~ios-marketing-light.png b/transcopied/Assets.xcassets/AppIconLight.appiconset/AppIcon~ios-marketing-light.png deleted file mode 100644 index 5218bab..0000000 Binary files a/transcopied/Assets.xcassets/AppIconLight.appiconset/AppIcon~ios-marketing-light.png and /dev/null differ diff --git a/transcopied/Assets.xcassets/AppIconLight.appiconset/Contents.json b/transcopied/Assets.xcassets/AppIconLight.appiconset/Contents.json deleted file mode 100644 index 1bb0228..0000000 --- a/transcopied/Assets.xcassets/AppIconLight.appiconset/Contents.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "images" : [ - { - "filename" : "AppIcon~ios-marketing-light.png", - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/transcopied/CopiedEditorView.swift b/transcopied/CopiedEditorView.swift index 3638dfa..0f797ca 100644 --- a/transcopied/CopiedEditorView.swift +++ b/transcopied/CopiedEditorView.swift @@ -13,60 +13,78 @@ struct CopiedEditorView: View { @Environment(\.presentationMode) var presentationMode: Binding @Environment(\.modelContext) private var modelContext @Bindable var item: CopiedItem + @State var title: String? + @FocusState private var editorFocused: Bool @State private var bottomBarPlacement: ToolbarItemPlacement = .bottomBar + @State private var copiedHapticTriggered: Bool = false var body: some View { VStack { - TextField(text: $item.title, label: { - EmptyView() - }) - .font(.title2) - .frame(maxWidth: /*@START_MENU_TOKEN@*/ .infinity/*@END_MENU_TOKEN@*/) + TextField(text: Binding($item.title, nilAs: ""), label: { EmptyView() }) + .font(.title2) Divider().padding(.vertical, 5).foregroundStyle(.primary) HStack { Text(item.type.rawValue) + Text(" - ") + - Text("\(item.content.count) characters") - }.frame(maxWidth: .infinity, alignment: .leading).foregroundStyle(.secondary).font(.caption2) - TextEditor(text: $item.content) - .frame(maxWidth: /*@START_MENU_TOKEN@*/ .infinity/*@END_MENU_TOKEN@*/, maxHeight: .infinity, alignment: .topLeading) - .padding(.top).foregroundStyle(.primary) + Text("\(item.content?.count ?? 0) characters") + } + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundStyle(.secondary) + .font(.caption2) + + TextEditor(text: Binding($item.content, nilAs: "")) + .frame( + // maxWidth: .infinity, + maxHeight: .infinity + ) +// .padding(.top) + .foregroundStyle(.primary) .focused($editorFocused) .onChange(of: editorFocused) { bottomBarPlacement = editorFocused ? .keyboard : .bottomBar } } + .accessibilityAction(.magicTap) { setClipboard() } + .navigationTitle("Edit") .padding(.horizontal) .toolbar { ToolbarItemGroup(placement: .keyboard) { - Button(action: setClipboard) { + Button(action: { + setClipboard() + copiedHapticTriggered.toggle() + }, label: { Label("Copy", systemImage: "square.and.arrow.down.on.square") - } + .sensoryFeedback(.success, trigger: copiedHapticTriggered) + }) Spacer() Spacer() Menu { Button(role: .destructive, action: deleteItem, label: { Label("Delete", systemImage: "trash") }) } label: { - Button(role: .destructive, action: deleteItem, label: { Label("Delete", systemImage: "ellipsis") }) + Button(action: {}, label: { Label("More", systemImage: "ellipsis") }) } } } .toolbar { ToolbarItemGroup(placement: .bottomBar) { - Button(action: setClipboard) { + Button(action: { + setClipboard() + copiedHapticTriggered.toggle() + }, label: { Label("Copy", systemImage: "square.and.arrow.down.on.square") - } + .sensoryFeedback(.success, trigger: copiedHapticTriggered) + }) Spacer() Spacer() Menu { Button(role: .destructive, action: deleteItem, label: { Label("Delete", systemImage: "trash") }) } label: { - Button(role: .destructive, action: deleteItem, label: { Label("More", systemImage: "ellipsis") }) + Button(action: {}, label: { Label("More", systemImage: "ellipsis") }) } - .frame(maxWidth: .infinity, maxHeight: .infinity) + .frame(minWidth: 44.0, maxWidth: .infinity, maxHeight: .infinity) } } } @@ -79,24 +97,13 @@ struct CopiedEditorView: View { } private func setClipboard() { - return UIPasteboard.general.setValue(item.content, forPasteboardType: UTType.plainText.identifier) - } - - private func randomAlphanumericString(_ length: Int) -> String { - let aln = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - return (0 ..< length).map { - _ in String(aln.randomElement()!) - }.reduce("", +) - } - - private func relativeDateFmt(_ d: Date) -> String { - let fmt: RelativeDateTimeFormatter = RelativeDateTimeFormatter() - fmt.unitsStyle = .abbreviated - return fmt.localizedString(fromTimeInterval: Date.now.distance(to: d)) + if item.content != nil { + UIPasteboard.general.setValue(item.content as Any, forPasteboardType: UTType.plainText.identifier) + } } } #Preview { - CopiedEditorView(item: CopiedItem(content: "Testing 123", timestamp: Date(), type: CopiedItemType.Text)) + CopiedEditorView(item: CopiedItem(content: "Testing 123", title: "", timestamp: Date(), type: CopiedItemType.text)) .modelContainer(for: CopiedItem.self, inMemory: true) } diff --git a/transcopied/CopiedItem.swift b/transcopied/CopiedItem.swift index eace3df..4afb994 100644 --- a/transcopied/CopiedItem.swift +++ b/transcopied/CopiedItem.swift @@ -7,29 +7,58 @@ import Foundation import SwiftData +import SwiftUI enum CopiedItemType: String, Codable { - case Text = "TXT" - case URL = "URL" - case File = "FILE" + case text = "TXT" + case url = "URL" + case file = "FILE" } @Model final class CopiedItem { - var title: String = "Untitled" - var content: String = "" - var timestamp: Date = Date.init(timeIntervalSinceNow: TimeInterval(0)) - var type: CopiedItemType = CopiedItemType.Text + var title: String? + var content: String? + var timestamp: Date = Date(timeIntervalSinceNow: TimeInterval(0)) + var type: CopiedItemType = CopiedItemType.text - init(content: String, title: String? = nil, timestamp: Date, type: CopiedItemType) { + init(content: String?, title: String?, timestamp: Date, type: CopiedItemType) { self.content = content self.timestamp = timestamp self.type = type - if title == nil { - self.title = "Untitled" - } - else { - self.title = title! - } + self.title = title + } +} + +public extension Binding { + init(_ source: Binding, _ defaultValue: Value) { + self.init(get: { + if source.wrappedValue == nil { + source.wrappedValue = defaultValue + } + return source.wrappedValue ?? defaultValue + }, set: { + source.wrappedValue = $0 + }) + } + + init(isNotNil source: Binding, defaultValue: T) where Value == Bool { + self.init(get: { source.wrappedValue != nil }, + set: { source.wrappedValue = $0 ? defaultValue : nil }) + } +} + +public extension Binding where Value: Equatable { + init(_ source: Binding, nilAs nilValue: Value) { + self.init( + get: { source.wrappedValue ?? nilValue }, + set: { newValue in + if newValue == nilValue { + source.wrappedValue = nil + } + else { + source.wrappedValue = newValue + } + }) } } diff --git a/transcopied/CopiedItemList.swift b/transcopied/CopiedItemList.swift index ddca1f5..f63fd03 100644 --- a/transcopied/CopiedItemList.swift +++ b/transcopied/CopiedItemList.swift @@ -7,13 +7,31 @@ import SwiftData import SwiftUI -struct CopiedItemRow: View { - var item: CopiedItem - private func relativeDateFmt(_ d: Date) -> String { - let fmt: RelativeDateTimeFormatter = RelativeDateTimeFormatter() - fmt.unitsStyle = .abbreviated - return fmt.localizedString(fromTimeInterval: Date.now.distance(to: d)) +struct ConditionalRowText: View { + var main: String? + var alt: String? + var def: String = "Tap to edit" + + var body: some View { + Text(calc(main: main, alt: alt, fallback: def)) + } + + func calc(main: String?, alt: String?, fallback: String) -> String { + if main == nil, alt == nil { + return fallback + } + else if alt != nil { + return main != nil ? String(main!.prefix(100)) : String(alt!.prefix(100)) + } + else { + // main should be safe to use + return String(main!.prefix(100)) + } } +} + +struct CopiedItemRow: View { + @Bindable var item: CopiedItem var body: some View { HStack(alignment: .top) { @@ -26,8 +44,7 @@ struct CopiedItemRow: View { .frame(maxHeight: .infinity, alignment: .center) VStack { HStack { - let rowtext = item.title == "Untitled" ? String(item.content.prefix(100)) : item.title - Text(rowtext) + ConditionalRowText(main: item.title, alt: item.content, def: "Empty Clipping! Tap to edit") .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading) .fixedSize(horizontal: false, vertical: true) .lineLimit(8) @@ -35,7 +52,7 @@ struct CopiedItemRow: View { HStack { Image(systemName: "info.circle") .symbolRenderingMode(.monochrome) - Text("\(item.content.count) characters") + Text("\(item.content?.count ?? 0) characters") Image(systemName: "clock") .symbolRenderingMode(.monochrome) Text(relativeDateFmt(item.timestamp)) @@ -49,6 +66,12 @@ struct CopiedItemRow: View { .padding(.leading) .frame(maxHeight: .infinity, alignment: .center) } + + private func relativeDateFmt(_ date: Date) -> String { + let fmt = RelativeDateTimeFormatter() + fmt.unitsStyle = .abbreviated + return fmt.localizedString(fromTimeInterval: Date.now.distance(to: date)) + } } struct CopiedItemList: View { @@ -60,7 +83,7 @@ struct CopiedItemList: View { List { ForEach(items) { item in NavigationLink { - CopiedEditorView(item: item) + CopiedEditorView(item: item, title: item.title) } label: { CopiedItemRow(item: item) } @@ -75,33 +98,34 @@ struct CopiedItemList: View { } ToolbarItem { Button(action: addItem) { - Label("Add Item", systemImage: "plus") + Label("Add Clipping", systemImage: "plus") } } } .toolbar { ToolbarItemGroup(placement: .bottomBar) { - Button("New", systemImage: "square.and.arrow.down", action: addItem) + Button("Paste", systemImage: "square.and.arrow.down", action: addItem) + .accessibilityLabel("Add Clipping") Spacer() -// Text("Clippings").font(.caption) Spacer() - Image(systemName: "slider.horizontal.3").imageScale(.medium) - .foregroundStyle(.primary) + NavigationLink { + AppDetails() + } label: { + Image(systemName: "slider.horizontal.3") + .imageScale(.medium) + .foregroundStyle(.primary) + } } } } + .accessibilityAction(.magicTap) { addItem() } } private func addItem() { withAnimation { let content = getClipboard() - if content != nil { - let newItem = CopiedItem(content: content!, timestamp: Date(), type: .Text) - modelContext.insert(newItem) - } - else { - - } + let newItem = CopiedItem(content: content, title: nil, timestamp: Date(), type: .text) + modelContext.insert(newItem) } } @@ -115,24 +139,8 @@ struct CopiedItemList: View { private func getClipboard() -> String? { let pasteboard = UIPasteboard.general - if let data = pasteboard.string { - return data - } -// return randomAlphanumericString(6) - return nil - } - - private func randomAlphanumericString(_ length: Int) -> String { - let aln = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - return (0 ..< length).map { - _ in String(aln.randomElement()!) - }.reduce("", +) - } - - private func relativeDateFmt(_ d: Date) -> String { - let fmt: RelativeDateTimeFormatter = RelativeDateTimeFormatter() - fmt.unitsStyle = .abbreviated - return fmt.localizedString(fromTimeInterval: Date.now.distance(to: d)) + let data = pasteboard.string + return data } } diff --git a/transcopied/Item.swift b/transcopied/Item.swift deleted file mode 100644 index 9d8191a..0000000 --- a/transcopied/Item.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Item.swift -// nipper -// -// Created by Dakota Lorance on 11/26/23. -// - -import Foundation -import SwiftData - -@Model -final class Item { - var timestamp: Date - - init(timestamp: Date) { - self.timestamp = timestamp - } -} diff --git a/transcopied/ModalMessage.swift b/transcopied/ModalMessage.swift new file mode 100644 index 0000000..291a078 --- /dev/null +++ b/transcopied/ModalMessage.swift @@ -0,0 +1,66 @@ +// +// ModalMessage.swift +// Transcopied +// +// Created by Dakota Lorance on 11/30/23. +// + +import SwiftUI + +@Observable +class ModalModel { + var hasFocus: Bool = false +} + +struct ModalMessageView: View { + var label: String + var message: String + @FocusState var isFocused + + var body: some View { + GroupBox(label: Text(label), content: { + Text(message + " \(isFocused)") + .focusable(true, interactions: [.activate, .edit, .automatic]) + .focused($isFocused) + }) + } + + func focus(value: Bool) {} +} + +struct ModalMessage: ViewModifier { + @FocusState var appeared: Bool + @State var trigger: Bool + @State var dismiss: Bool = false + + func body(content: Content) -> some View { + content.overlay(alignment: .bottomTrailing) { + if dismiss, !trigger { + EmptyView() + } + else { + ModalMessageView(label: "Label", message: "Message") + .aspectRatio(2 / 5, contentMode: .fit) + .containerRelativeFrame([.vertical, .horizontal], alignment: .top) + .onChange(of: appeared) { + dismiss = appeared ? false : true + } + } + } + } +} + +#Preview { + Group { + @State var showToast: Bool = false + Group { + Text("Test") + .frame(maxWidth: .infinity, maxHeight: .infinity) + .border(Color.black, width: 2.0) + .onHover(perform: { hovering in + showToast = hovering + }) + .modifier(ModalMessage(trigger: showToast)) + } + } +} diff --git a/transcopied/Transcopied.swift b/transcopied/Transcopied.swift index d99ae83..8c211ea 100644 --- a/transcopied/Transcopied.swift +++ b/transcopied/Transcopied.swift @@ -5,8 +5,8 @@ // Created by Dakota Lorance on 11/26/23. // -import SwiftUI import SwiftData +import SwiftUI @main struct Transcopied: App { @@ -14,11 +14,16 @@ struct Transcopied: App { let schema = Schema([ CopiedItem.self, ]) - let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true) - + let modelConfiguration = ModelConfiguration( + schema: schema, + isStoredInMemoryOnly: false, + allowsSave: true, + cloudKitDatabase: ModelConfiguration.CloudKitDatabase.private("iCloud.Transcopied") + ) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) - } catch { + } + catch { fatalError("Could not create ModelContainer: \(error)") } }()