Refactor wallet-related modules

This commit is contained in:
Peter 2019-10-08 14:30:24 +04:00
parent 5fed1fbeb1
commit d2c54825f0
230 changed files with 4534 additions and 2435 deletions

7
BUCK
View File

@ -1,3 +1,7 @@
load("//Config:utils.bzl",
"library_configs",
)
load("//Config:configs.bzl",
"app_binary_configs",
"share_extension_configs",
@ -7,7 +11,6 @@ load("//Config:configs.bzl",
"intents_extension_configs",
"watch_extension_binary_configs",
"watch_binary_configs",
"library_configs",
"info_plist_substitutions",
"app_info_plist_substitutions",
"share_extension_info_plist_substitutions",
@ -43,7 +46,9 @@ resource_dependencies = [
"//submodules/LegacyComponents:LegacyComponentsResources",
"//submodules/TelegramUI:TelegramUIAssets",
"//submodules/TelegramUI:TelegramUIResources",
"//submodules/WalletUI:WalletUIAssets",
"//submodules/WalletUI:WalletUIResources",
"//submodules/OverlayStatusController:OverlayStatusControllerResources",
"//:AppResources",
"//:AppStringResources",
"//:InfoPlistStringResources",

View File

@ -1,4 +1,7 @@
load("//Config:configs.bzl", "library_configs", "dynamic_library_configs", "info_plist_substitutions")
load("//Config:utils.bzl",
"library_configs",
"dynamic_library_configs",
)
def apple_lib(
name,

View File

@ -1,190 +1,25 @@
load("//Config:utils.bzl",
"config_with_updated_linker_flags",
"configs_with_config",
"merge_dict",
"DEVELOPMENT_LANGUAGE",
"SHARED_CONFIGS",
"ALL_LOAD_LINKER_FLAG",
"read_config_nonempty",
"optimization_config",
"add_provisioning_profile_specifier",
"add_codesign_identity",
"get_build_number",
"get_short_version",
"bundle_identifier",
"get_development_team",
"get_provisioning_profile",
"get_codesign_entitlements",
)
load("//Config:utils.bzl", "config_with_updated_linker_flags", "configs_with_config")
load("//Config:app_configuration.bzl", "appConfig")
DEVELOPMENT_LANGUAGE = "en"
def merge_dict(a, b):
d = {}
d.update(a)
d.update(b)
return d
def pretty(dict, current = ""):
current = "\n"
indent = 0
for key, value in dict.items():
current = current + str(key) + ": "
if type(value) == type({}):
current = current + "\n"
indent = 1
for key2, value2 in value.items():
current = current + "\t" * indent + str(key2)
current = current + ": " + str(value2) + "\n"
else:
current = current + "\t" * (indent + 1) + str(value) + "\n"
return current
SHARED_CONFIGS = {
"IPHONEOS_DEPLOYMENT_TARGET": "9.0",
"SDKROOT": "iphoneos",
"GCC_OPTIMIZATION_LEVEL": "0",
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
"ONLY_ACTIVE_ARCH": "YES",
"LD_RUNPATH_SEARCH_PATHS": "@executable_path/Frameworks",
"ENABLE_BITCODE": "NO",
}
def optimization_config():
return {
"SWIFT_OPTIMIZATION_LEVEL": native.read_config("custom", "optimization"),
}
# Adding `-all_load` to our binaries works around https://bugs.swift.org/browse/SR-6004.
ALL_LOAD_LINKER_FLAG = "-all_load"
def read_config_nonempty(name):
value = native.read_config("custom", name)
if value == None:
fail("Configuration parameter custom.%s should be defined" % name)
elif len(value) == 0:
fail("Configuration parameter custom.%s should not be empty" % name)
else:
return value
def get_codesign_identity(environment):
if environment == "development":
return read_config_nonempty("developmentCodeSignIdentity")
elif environment == "distribution":
return read_config_nonempty("distributionCodeSignIdentity")
else:
fail("Unknown environment " + environment)
def get_provisioning_profile(environment, type):
if type == "app":
return read_config_nonempty(environment + "ProvisioningProfileApp")
elif type == "share":
return read_config_nonempty(environment + "ProvisioningProfileExtensionShare")
elif type == "widget":
return read_config_nonempty(environment + "ProvisioningProfileExtensionWidget")
elif type == "notification_service":
return read_config_nonempty(environment + "ProvisioningProfileExtensionNotificationService")
elif type == "notification_content":
return read_config_nonempty(environment + "ProvisioningProfileExtensionNotificationContent")
elif type == "intents":
return read_config_nonempty(environment + "ProvisioningProfileExtensionIntents")
elif type == "watch_app":
return read_config_nonempty(environment + "ProvisioningProfileWatchApp")
elif type == "watch_extension":
return read_config_nonempty(environment + "ProvisioningProfileWatchExtension")
else:
fail("Unknown provisioning profile type " + type)
def get_development_team():
return read_config_nonempty("developmentTeam")
def add_item_to_subdict(superdict, key, subkey, item):
subdict = dict(superdict[key])
subdict[subkey] = item
superdict[key] = subdict
valid_configurations = ["Debug", "Profile", "Release"]
def add_provisioning_profile_specifier(configs, type):
for configuration in configs:
if configuration not in valid_configurations:
fail("Unknown configuration " + configuration)
configs = dict(configs)
for configuration in valid_configurations:
if configuration == "Debug":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="development", type=type))
elif configuration == "Profile":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="development", type=type))
elif configuration == "Release":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="distribution", type=type))
return configs
def add_codesign_identity(configs):
for configuration in configs:
if configuration not in valid_configurations:
fail("Unknown configuration " + configuration)
configs = dict(configs)
for configuration in valid_configurations:
if configuration == "Debug":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="development"))
elif configuration == "Profile":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="development"))
elif configuration == "Release":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="distribution"))
return configs
def get_codesign_entitlements(type):
if type == "app":
return read_config_nonempty("entitlementsApp")
elif type == "share":
return read_config_nonempty("entitlementsExtensionShare")
elif type == "widget":
return read_config_nonempty("entitlementsExtensionWidget")
elif type == "notification_service":
return read_config_nonempty("entitlementsExtensionNotificationService")
elif type == "notification_content":
return read_config_nonempty("entitlementsExtensionNotificationContent")
elif type == "intents":
return read_config_nonempty("entitlementsExtensionIntents")
else:
fail("unknown provisioning profile type")
def get_build_number():
return read_config_nonempty("buildNumber")
def get_short_version():
return read_config_nonempty("appVersion")
def bundle_identifier(suffix):
return read_config_nonempty("baseApplicationBundleId") + suffix
def library_configs():
lib_specific_config = {
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
# Setting SKIP_INSTALL to NO for static library configs would create
# create a generic xcode archive which can not be uploaded the app store
# https://developer.apple.com/library/archive/technotes/tn2215/_index.html
"SKIP_INSTALL": "YES",
}
library_config = merge_dict(SHARED_CONFIGS, lib_specific_config)
library_config = merge_dict(library_config, optimization_config())
configs = {
"Debug": library_config,
"Profile": library_config,
"Release": library_config,
}
return configs
def dynamic_library_configs():
lib_specific_config = {
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
# Setting SKIP_INSTALL to NO for static library configs would create
# create a generic xcode archive which can not be uploaded the app store
# https://developer.apple.com/library/archive/technotes/tn2215/_index.html
"SKIP_INSTALL": "YES",
"MACH_O_TYPE": "mh_dylib",
"CODE_SIGNING_ALLOWED": "NO",
}
library_config = merge_dict(SHARED_CONFIGS, lib_specific_config)
library_config = merge_dict(library_config, optimization_config())
#library_config = config_with_updated_linker_flags(library_config, ALL_LOAD_LINKER_FLAG)
configs = {
"Debug": library_config,
"Profile": library_config,
"Release": library_config,
}
return configs
load("//Config:app_configuration.bzl",
"appConfig",
)
def app_binary_configs():
config = {

View File

@ -33,3 +33,170 @@ def config_with_updated_linker_flags(config, other_linker_flags, config_key=OTHE
new_config[config_key] = '$(inherited) ' + other_linker_flags
return new_config
def merge_dict(a, b):
d = {}
d.update(a)
d.update(b)
return d
DEVELOPMENT_LANGUAGE = "en"
SHARED_CONFIGS = {
"IPHONEOS_DEPLOYMENT_TARGET": "9.0",
"SDKROOT": "iphoneos",
"GCC_OPTIMIZATION_LEVEL": "0",
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
"ONLY_ACTIVE_ARCH": "YES",
"LD_RUNPATH_SEARCH_PATHS": "@executable_path/Frameworks",
"ENABLE_BITCODE": "NO",
}
# Adding `-all_load` to our binaries works around https://bugs.swift.org/browse/SR-6004.
ALL_LOAD_LINKER_FLAG = "-all_load"
def optimization_config():
return {
"SWIFT_OPTIMIZATION_LEVEL": native.read_config("custom", "optimization"),
}
def read_config_nonempty(name):
value = native.read_config("custom", name)
if value == None:
fail("Configuration parameter custom.%s should be defined" % name)
elif len(value) == 0:
fail("Configuration parameter custom.%s should not be empty" % name)
else:
return value
def get_codesign_identity(environment):
if environment == "development":
return read_config_nonempty("developmentCodeSignIdentity")
elif environment == "distribution":
return read_config_nonempty("distributionCodeSignIdentity")
else:
fail("Unknown environment " + environment)
def get_development_team():
return read_config_nonempty("developmentTeam")
def add_item_to_subdict(superdict, key, subkey, item):
subdict = dict(superdict[key])
subdict[subkey] = item
superdict[key] = subdict
valid_configurations = ["Debug", "Profile", "Release"]
def add_provisioning_profile_specifier(configs, type):
for configuration in configs:
if configuration not in valid_configurations:
fail("Unknown configuration " + configuration)
configs = dict(configs)
for configuration in valid_configurations:
if configuration == "Debug":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="development", type=type))
elif configuration == "Profile":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="development", type=type))
elif configuration == "Release":
add_item_to_subdict(configs, configuration, "PROVISIONING_PROFILE_SPECIFIER", get_provisioning_profile(environment="distribution", type=type))
return configs
def add_codesign_identity(configs):
for configuration in configs:
if configuration not in valid_configurations:
fail("Unknown configuration " + configuration)
configs = dict(configs)
for configuration in valid_configurations:
if configuration == "Debug":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="development"))
elif configuration == "Profile":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="development"))
elif configuration == "Release":
add_item_to_subdict(configs, configuration, "CODE_SIGN_IDENTITY", get_codesign_identity(environment="distribution"))
return configs
def get_build_number():
return read_config_nonempty("buildNumber")
def get_short_version():
return read_config_nonempty("appVersion")
def bundle_identifier(suffix):
return read_config_nonempty("baseApplicationBundleId") + suffix
def library_configs():
lib_specific_config = {
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
# Setting SKIP_INSTALL to NO for static library configs would create
# create a generic xcode archive which can not be uploaded the app store
# https://developer.apple.com/library/archive/technotes/tn2215/_index.html
"SKIP_INSTALL": "YES",
}
library_config = merge_dict(SHARED_CONFIGS, lib_specific_config)
library_config = merge_dict(library_config, optimization_config())
configs = {
"Debug": library_config,
"Profile": library_config,
"Release": library_config,
}
return configs
def dynamic_library_configs():
lib_specific_config = {
"SWIFT_WHOLE_MODULE_OPTIMIZATION": "NO",
# Setting SKIP_INSTALL to NO for static library configs would create
# create a generic xcode archive which can not be uploaded the app store
# https://developer.apple.com/library/archive/technotes/tn2215/_index.html
"SKIP_INSTALL": "YES",
"MACH_O_TYPE": "mh_dylib",
"CODE_SIGNING_ALLOWED": "NO",
}
library_config = merge_dict(SHARED_CONFIGS, lib_specific_config)
library_config = merge_dict(library_config, optimization_config())
configs = {
"Debug": library_config,
"Profile": library_config,
"Release": library_config,
}
return configs
def get_provisioning_profile(environment, type):
if type == "app":
return read_config_nonempty(environment + "ProvisioningProfileApp")
elif type == "share":
return read_config_nonempty(environment + "ProvisioningProfileExtensionShare")
elif type == "widget":
return read_config_nonempty(environment + "ProvisioningProfileExtensionWidget")
elif type == "notification_service":
return read_config_nonempty(environment + "ProvisioningProfileExtensionNotificationService")
elif type == "notification_content":
return read_config_nonempty(environment + "ProvisioningProfileExtensionNotificationContent")
elif type == "intents":
return read_config_nonempty(environment + "ProvisioningProfileExtensionIntents")
elif type == "watch_app":
return read_config_nonempty(environment + "ProvisioningProfileWatchApp")
elif type == "watch_extension":
return read_config_nonempty(environment + "ProvisioningProfileWatchExtension")
else:
fail("Unknown provisioning profile type " + type)
def get_codesign_entitlements(type):
if type == "app":
return read_config_nonempty("entitlementsApp")
elif type == "share":
return read_config_nonempty("entitlementsExtensionShare")
elif type == "widget":
return read_config_nonempty("entitlementsExtensionWidget")
elif type == "notification_service":
return read_config_nonempty("entitlementsExtensionNotificationService")
elif type == "notification_content":
return read_config_nonempty("entitlementsExtensionNotificationContent")
elif type == "intents":
return read_config_nonempty("entitlementsExtensionIntents")
else:
fail("unknown provisioning profile type")

58
Config/wallet_configs.bzl Normal file
View File

@ -0,0 +1,58 @@
load("//Config:utils.bzl",
"config_with_updated_linker_flags",
"configs_with_config",
"merge_dict",
"DEVELOPMENT_LANGUAGE",
"SHARED_CONFIGS",
"ALL_LOAD_LINKER_FLAG",
"optimization_config",
"add_provisioning_profile_specifier",
"add_codesign_identity",
"get_build_number",
"get_short_version",
"bundle_identifier",
"get_development_team",
"get_provisioning_profile",
"get_codesign_entitlements",
)
load("//Config:app_configuration.bzl",
"appConfig"
)
def app_binary_configs():
config = {
"ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES": "YES",
"DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
"PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier(suffix=""),
"CODE_SIGN_ENTITLEMENTS": get_codesign_entitlements("app"),
"DEVELOPMENT_TEAM": get_development_team(),
"ASSETCATALOG_COMPILER_APPICON_NAME": "AppIconLLC",
"BUILD_NUMBER": get_build_number(),
"PRODUCT_BUNDLE_SHORT_VERSION": get_short_version(),
"APP_NAME": "TON Wallet",
"PRODUCT_NAME": "TON Wallet",
"TARGETED_DEVICE_FAMILY": "1,2",
}
config = merge_dict(SHARED_CONFIGS, config)
config = merge_dict(config, optimization_config())
config = config_with_updated_linker_flags(config, ALL_LOAD_LINKER_FLAG)
configs = configs_with_config(config)
configs = add_provisioning_profile_specifier(configs, "app")
configs = add_codesign_identity(configs)
return configs
def app_info_plist_substitutions():
substitutions = {
"DEVELOPMENT_LANGUAGE": DEVELOPMENT_LANGUAGE,
"EXECUTABLE_NAME": "TON Wallet",
"PRODUCT_BUNDLE_IDENTIFIER": bundle_identifier(suffix=""),
"PRODUCT_NAME": "TON Wallet",
"APP_NAME": "TON Wallet",
"CURRENT_PROJECT_VERSION": "1",
"BUILD_NUMBER": get_build_number(),
"PRODUCT_BUNDLE_SHORT_VERSION": get_short_version(),
"ASSETCATALOG_COMPILER_APPICON_NAME": "AppIconLLC",
"TARGETED_DEVICE_FAMILY": "1,2",
}
return substitutions

View File

@ -50,6 +50,24 @@ BUCK_OPTIONS=\
--config custom.developmentProvisioningProfileWatchExtension="${DEVELOPMENT_PROVISIONING_PROFILE_WATCH_EXTENSION}" \
--config custom.distributionProvisioningProfileWatchExtension="${DISTRIBUTION_PROVISIONING_PROFILE_WATCH_EXTENSION}"
WALLET_BUCK_OPTIONS=\
--config custom.appVersion="1.0" \
--config custom.developmentCodeSignIdentity="${DEVELOPMENT_CODE_SIGN_IDENTITY}" \
--config custom.distributionCodeSignIdentity="${DISTRIBUTION_CODE_SIGN_IDENTITY}" \
--config custom.developmentTeam="${DEVELOPMENT_TEAM}" \
--config custom.baseApplicationBundleId="${WALLET_BUNDLE_ID}" \
--config custom.buildNumber="${BUILD_NUMBER}" \
--config custom.entitlementsApp="${WALLET_ENTITLEMENTS_APP}" \
--config custom.developmentProvisioningProfileApp="${WALLET_DEVELOPMENT_PROVISIONING_PROFILE_APP}" \
--config custom.distributionProvisioningProfileApp="${WALLET_DISTRIBUTION_PROVISIONING_PROFILE_APP}" \
--config custom.apiId="${API_ID}" \
--config custom.apiHash="${API_HASH}" \
--config custom.hockeyAppId="${HOCKEYAPP_ID}" \
--config custom.isInternalBuild="${IS_INTERNAL_BUILD}" \
--config custom.isAppStoreBuild="${IS_APPSTORE_BUILD}" \
--config custom.appStoreId="${APPSTORE_ID}" \
--config custom.appSpecificUrlScheme="${APP_SPECIFIC_URL_SCHEME}"
BUCK_THREADS_OPTIONS=--config build.threads=$(shell sysctl -n hw.logicalcpu)
BUCK_CACHE_OPTIONS=
@ -138,7 +156,7 @@ build_wallet_debug_arm64: check_env
//submodules/AsyncDisplayKit:AsyncDisplayKit#shared,iphoneos-arm64 \
//submodules/Display:Display#dwarf-and-dsym,shared,iphoneos-arm64 \
//submodules/Display:Display#shared,iphoneos-arm64 \
${BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS} ${BUCK_THREADS_OPTIONS} ${BUCK_CACHE_OPTIONS}
${WALLET_BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS} ${BUCK_THREADS_OPTIONS} ${BUCK_CACHE_OPTIONS}
build_debug_armv7: check_env
$(BUCK) build \
@ -392,8 +410,12 @@ project: check_env kill_xcode
$(BUCK) project //:workspace --config custom.mode=project ${BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS}
open Telegram_Buck.xcworkspace
wallet_deps: check_env
$(BUCK) query "deps(//Wallet:AppPackage)" \
${WALLET_BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS}
wallet_project: check_env kill_xcode
$(BUCK) project //Wallet:workspace --config custom.mode=project ${BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS}
$(BUCK) project //Wallet:workspace --config custom.mode=project ${WALLET_BUCK_OPTIONS} ${BUCK_DEBUG_OPTIONS}
open Wallet/WalletWorkspace.xcworkspace
project_opt: check_env kill_xcode

View File

@ -1,11 +1,13 @@
load("//Config:configs.bzl",
"app_binary_configs",
load("//Config:utils.bzl",
"library_configs",
"info_plist_substitutions",
)
load("//Config:wallet_configs.bzl",
"app_binary_configs",
"app_info_plist_substitutions",
)
load("//Config:buck_rule_macros.bzl",
"apple_lib",
"framework_binary_dependencies",
"framework_bundle_dependencies",
)
@ -13,46 +15,25 @@ load("//Config:buck_rule_macros.bzl",
framework_dependencies = [
"//submodules/MtProtoKit:MtProtoKit",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
]
resource_dependencies = [
"//submodules/TelegramUI:TelegramUIAssets",
"//submodules/TelegramUI:TelegramUIResources",
"//:AppResources",
"//:AppStringResources",
"//:InfoPlistStringResources",
"//:AppIntentVocabularyResources",
"//:Icons",
"//:AdditionalIcons",
"//:LaunchScreen",
"//submodules/WalletUI:WalletUIResources",
"//submodules/WalletUI:WalletUIAssets",
"//submodules/OverlayStatusController:OverlayStatusControllerResources",
":StringResources",
":InfoPlistStringResources",
":Icons",
":LaunchScreen",
]
apple_resource(
name = "AppResources",
files = glob([
"Telegram-iOS/Resources/**/*",
], exclude = ["Telegram-iOS/Resources/**/.*"]),
visibility = ["PUBLIC"],
)
apple_resource(
name = "AppStringResources",
name = "StringResources",
files = [],
variants = glob([
"Telegram-iOS/*.lproj/Localizable.strings",
]),
visibility = ["PUBLIC"],
)
apple_resource(
name = "AppIntentVocabularyResources",
files = [],
variants = glob([
"Telegram-iOS/*.lproj/AppIntentVocabulary.plist",
"Strings/*.lproj/Localizable.strings",
]),
visibility = ["PUBLIC"],
)
@ -61,7 +42,7 @@ apple_resource(
name = "InfoPlistStringResources",
files = [],
variants = glob([
"Telegram-iOS/*.lproj/InfoPlist.strings",
"InfoPlistStrings/*.lproj/InfoPlist.strings",
]),
visibility = ["PUBLIC"],
)
@ -69,25 +50,16 @@ apple_resource(
apple_asset_catalog(
name = "Icons",
dirs = [
"Telegram-iOS/Icons.xcassets",
"Telegram-iOS/AppIcons.xcassets",
"Icons.xcassets",
],
app_icon = "AppIconLLC",
visibility = ["PUBLIC"],
)
apple_resource(
name = "AdditionalIcons",
files = glob([
"Telegram-iOS/*.png",
]),
visibility = ["PUBLIC"],
)
apple_resource(
name = "LaunchScreen",
files = [
"Telegram-iOS/Base.lproj/LaunchScreen.xib",
"LaunchScreen.xib",
],
visibility = ["PUBLIC"],
)
@ -95,8 +67,7 @@ apple_resource(
apple_library(
name = "AppLibrary",
visibility = [
"//:",
"//...",
"//Wallet:...",
],
configs = library_configs(),
swift_version = native.read_config("swift", "version"),
@ -110,6 +81,7 @@ apple_library(
],
deps = [
"//submodules/WalletUI:WalletUI",
"//submodules/BuildConfig:BuildConfig",
]
+ framework_binary_dependencies(framework_dependencies),
frameworks = [
@ -122,8 +94,7 @@ apple_library(
apple_binary(
name = "AppBinary",
visibility = [
"//:",
"//...",
"//Wallet:...",
],
configs = app_binary_configs(),
swift_version = native.read_config("swift", "version"),

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 665 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -0,0 +1,119 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "BlueNotificationIcon@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "BlueNotificationIcon@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Simple@58x58.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Simple@87x87.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Simple@80x80.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "BlueIcon@2x-1.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "BlueIcon@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "BlueIcon@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "BlueNotificationIcon.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "BlueNotificationIcon@2x-1.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Simple@29x29.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Simple@58x58-1.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Simple@40x40-1.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Simple@80x80-1.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "BlueIconIpad.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "BlueIconIpad@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "BlueIconLargeIpad@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Simple-iTunesArtwork.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"pre-rendered" : true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@ -12,166 +12,20 @@
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIcons</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>Black</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackIcon</string>
<string>BlackNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlackClassic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackClassicIcon</string>
<string>BlackClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlackFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackFilledIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>Blue</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueIcon</string>
<string>BlueNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlueClassic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueClassicIcon</string>
<string>BlueClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlueFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueFilledIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>WhiteFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>WhiteFilledIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconName</key>
<string>AppIconLLC</string>
<string>AppIconWallet</string>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
</dict>
<key>CFBundleIcons~ipad</key>
<dict>
<key>CFBundleAlternateIcons</key>
<dict>
<key>Black</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackIconIpad</string>
<string>BlackIconLargeIpad</string>
<string>BlackNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlackClassic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackClassicIconIpad</string>
<string>BlackClassicIconLargeIpad</string>
<string>BlackClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlackFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlackFilledIconIpad</string>
<string>BlackFilledIconLargeIpad</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>Blue</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueIconIpad</string>
<string>BlueIconLargeIpad</string>
<string>BlueNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlueClassic</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueClassicIconIpad</string>
<string>BlueClassicIconLargeIpad</string>
<string>BlueClassicNotificationIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>BlueFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>BlueFilledIconIpad</string>
<string>BlueFilledIconLargeIpad</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
<key>WhiteFilled</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>WhiteFilledIcon</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
</dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconName</key>
<string>AppIconLLC</string>
<string>AppIconWallet</string>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
@ -190,27 +44,6 @@
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>telegram</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).compatibility</string>
<key>CFBundleURLSchemes</key>
<array>
<string>tg</string>
<string>$(APP_SPECIFIC_URL_SCHEME)</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
@ -226,38 +59,6 @@
<string>${BUILD_NUMBER}</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>instagram</string>
<string>comgooglemaps-x-callback</string>
<string>foursquare</string>
<string>here-location</string>
<string>yandexmaps</string>
<string>yandexnavi</string>
<string>comgooglemaps</string>
<string>youtube</string>
<string>twitter</string>
<string>vk</string>
<string>waze</string>
<string>googlechrome</string>
<string>googlechromes</string>
<string>firefox</string>
<string>touch-http</string>
<string>touch-https</string>
<string>yandexbrowser-open-url</string>
<string>vimeo</string>
<string>vine</string>
<string>coub</string>
<string>uber</string>
<string>citymapper</string>
<string>lyft</string>
<string>opera-http</string>
<string>opera-https</string>
<string>firefox-focus</string>
<string>ddgQuickLink</string>
<string>moovit</string>
<string>alook</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
@ -265,43 +66,8 @@
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSCameraUsageDescription</key>
<string>We need this so that you can take and share photos and videos.</string>
<key>NSContactsUsageDescription</key>
<string>Telegram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices.</string>
<key>NSFaceIDUsageDescription</key>
<string>You can use Face ID to unlock the app.</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>When you send your location to your friends, Telegram needs access to show them a map. You also need this to send locations from an Apple Watch.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>When you send your location to your friends, Telegram needs access to show them a map.</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need this so that you can record and share voice messages and videos with sound.</string>
<key>NSMotionUsageDescription</key>
<string>When you send your location to your friends, Telegram needs access to show them a map.</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need this so that you can share photos and videos from your photo library.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need this so that you can share photos and videos from your photo library.</string>
<key>NSSiriUsageDescription</key>
<string>You can use Siri to send messages.</string>
<key>NSUserActivityTypes</key>
<array>
<string>INSendMessageIntent</string>
<string>RemindAboutChatIntent</string>
</array>
<key>UIAppFonts</key>
<array>
<string>SFCompactRounded-Semibold.otf</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
<string>voip</string>
</array>
<string>For better security, please allow TON Wallet to use your Face ID to authenticate payments.</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
@ -338,29 +104,5 @@
<false/>
<key>UIViewGroupOpacity</key>
<false/>
<key>UTImportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Telegram iOS Color Theme File</string>
<key>UTTypeIconFiles</key>
<array>
<string>BlueIcon@3x.png</string>
</array>
<key>UTTypeIdentifier</key>
<string>org.telegram.Telegram-iOS.theme</string>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>tgios-theme</string>
</array>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -0,0 +1,5 @@
/* Localized versions of Info.plist keys */
"NSCameraUsageDescription" = "We need this so that you can take and share photos and videos.";
"NSPhotoLibraryUsageDescription" = "We need this so that you can share photos and videos from your photo library.";
"NSFaceIDUsageDescription" = "For better security, please allow TON Wallet to use your Face ID to authenticate payments.";

17
Wallet/LaunchScreen.xib Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="11163.2" systemVersion="16A239j" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11133"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="O8c-13-3vw">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</view>
</objects>
</document>

View File

@ -1,8 +1,9 @@
import UIKit
import Display
import TelegramPresentationData
import WalletUI
import SwiftSignalKit
import BuildConfig
import WalletUI
import WalletCore
private func encodeText(_ string: String, _ key: Int) -> String {
var result = ""
@ -119,10 +120,166 @@ private class ApplicationStatusBarHost: StatusBarHost {
}
}
private let records = Atomic<[WalletStateRecord]>(value: [])
private final class WalletStorageInterfaceImpl: WalletStorageInterface {
func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
return .single(records.with { $0 })
}
func getWalletRecords() -> Signal<[WalletStateRecord], NoError> {
return .single(records.with { $0 })
}
func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError> {
return .single(records.modify(f))
}
}
private final class WalletContextImpl: WalletContext {
let storage: WalletStorageInterface
let tonInstance: TonInstance
let keychain: TonKeychain
let presentationData: WalletPresentationData
var inForeground: Signal<Bool, NoError> {
return .single(true)
}
func getServerSalt() -> Signal<Data, WalletContextGetServerSaltError> {
return .single(Data())
}
func presentNativeController(_ controller: UIViewController) {
}
func idleTimerExtension() -> Disposable {
return EmptyDisposable
}
func openUrl(_ url: String) {
}
func shareUrl(_ url: String) {
}
func openPlatformSettings() {
}
func authorizeAccessToCamera(completion: @escaping () -> Void) {
completion()
}
func pickImage(completion: @escaping (UIImage) -> Void) {
}
init(basePath: String, config: String, blockchainName: String, navigationBarTheme: NavigationBarTheme) {
self.storage = WalletStorageInterfaceImpl()
self.tonInstance = TonInstance(
basePath: basePath,
config: config,
blockchainName: blockchainName,
proxy: nil
)
self.keychain = TonKeychain(encryptionPublicKey: {
return .single(Data())
}, encrypt: { data in
return .single(TonKeychainEncryptedData(publicKey: Data(), data: data))
}, decrypt: { data in
return .single(data.data)
})
let accentColor = UIColor(rgb: 0x007ee5)
self.presentationData = WalletPresentationData(
theme: WalletTheme(
info: WalletInfoTheme(
incomingFundsTitleColor: UIColor(rgb: 0x00b12c),
outgoingFundsTitleColor: UIColor(rgb: 0xff3b30)
), setup: WalletSetupTheme(
buttonFillColor: accentColor,
buttonForegroundColor: .white,
inputBackgroundColor: UIColor(rgb: 0xe9e9e9),
inputPlaceholderColor: UIColor(rgb: 0x818086),
inputTextColor: .black,
inputClearButtonColor: UIColor(rgb: 0x7b7b81).withAlphaComponent(0.8)
),
list: WalletListTheme(
itemPrimaryTextColor: .black,
itemSecondaryTextColor: UIColor(rgb: 0x8e8e93),
itemPlaceholderTextColor: UIColor(rgb: 0xc8c8ce),
itemDestructiveColor: UIColor(rgb: 0xff3b30),
itemAccentColor: accentColor,
itemDisabledTextColor: UIColor(rgb: 0x8e8e93),
plainBackgroundColor: .white,
blocksBackgroundColor: UIColor(rgb: 0xefeff4),
itemPlainSeparatorColor: UIColor(rgb: 0xc8c7cc),
itemBlocksBackgroundColor: .white,
itemBlocksSeparatorColor: UIColor(rgb: 0xc8c7cc),
itemHighlightedBackgroundColor: UIColor(rgb: 0xe5e5ea),
sectionHeaderTextColor: UIColor(rgb: 0x6d6d72),
freeTextColor: UIColor(rgb: 0x6d6d72),
freeTextErrorColor: UIColor(rgb: 0xcf3030),
inputClearButtonColor: UIColor(rgb: 0xcccccc)
),
statusBarStyle: .Black,
navigationBar: navigationBarTheme,
keyboardAppearance: .light,
alert: AlertControllerTheme(
backgroundType: .light,
backgroundColor: .white,
separatorColor: UIColor(white: 0.9, alpha: 1.0),
highlightedItemColor: UIColor(rgb: 0xe5e5ea),
primaryColor: .black,
secondaryColor: UIColor(rgb: 0x5e5e5e),
accentColor: accentColor,
destructiveColor: UIColor(rgb: 0xff3b30),
disabledColor: UIColor(rgb: 0xd0d0d0)
),
actionSheet: ActionSheetControllerTheme(
dimColor: UIColor(white: 0.0, alpha: 0.4),
backgroundType: .light,
itemBackgroundColor: .white,
itemHighlightedBackgroundColor: UIColor(white: 0.9, alpha: 1.0),
standardActionTextColor: accentColor,
destructiveActionTextColor: UIColor(rgb: 0xff3b30),
disabledActionTextColor: UIColor(rgb: 0xb3b3b3),
primaryTextColor: .black,
secondaryTextColor: UIColor(rgb: 0x5e5e5e),
controlAccentColor: accentColor,
controlColor: UIColor(rgb: 0x7e8791),
switchFrameColor: UIColor(rgb: 0xe0e0e0),
switchContentColor: UIColor(rgb: 0x77d572),
switchHandleColor: UIColor(rgb: 0xffffff)
)
), strings: WalletStrings(
primaryComponent: WalletStringsComponent(
languageCode: "en",
localizedName: "English",
pluralizationRulesCode: "en",
dict: [:]
),
secondaryComponent: nil,
groupingSeparator: " "
), dateTimeFormat: WalletPresentationDateTimeFormat(
timeFormat: .regular,
dateFormat: .dayFirst,
dateSeparator: ".",
decimalSeparator: ".",
groupingSeparator: " "
)
)
}
}
@objc(AppDelegate)
final class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
var mainWindow: Window1?
private var mainWindow: Window1?
private var walletContext: WalletContextImpl?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
let statusBarHost = ApplicationStatusBarHost()
@ -131,11 +288,61 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
hostView.containerView.backgroundColor = UIColor.white
self.window = window
let navigationController = NavigationController(mode: .single, theme: NavigationControllerTheme(presentationTheme: defaultPresentationTheme), backgroundDetailsMode: nil)
navigationController.setViewControllers([], animated: false)
let navigationBarTheme = NavigationBarTheme(
buttonColor: .blue,
disabledButtonColor: .gray,
primaryTextColor: .black,
backgroundColor: .lightGray,
separatorColor: .black,
badgeBackgroundColor: .red,
badgeStrokeColor: .red,
badgeTextColor: .white
)
let navigationController = NavigationController(
mode: .single,
theme: NavigationControllerTheme(
statusBar: .black,
navigationBar: navigationBarTheme,
emptyAreaColor: .white
), backgroundDetailsMode: nil
)
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let config =
"""
{
"liteservers": [
{
"ip": 1137658550,
"port": 4924,
"id": {
"@type": "pub.ed25519",
"key": "peJTw/arlRfssgTuf9BMypJzqOi7SXEqSPSWiEw2U1M="
}
}
],
"validator": {
"@type": "validator.config.global",
"zero_state": {
"workchain": -1,
"shard": -9223372036854775808,
"seqno": 0,
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=",
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo="
}
}
}
"""
let walletContext = WalletContextImpl(basePath: documentsPath, config: config, blockchainName: "testnet", navigationBarTheme: navigationBarTheme)
self.walletContext = walletContext
let splashScreen = WalletSplashScreen(context: walletContext, mode: .intro, walletCreatedPreloadState: nil)
navigationController.setViewControllers([splashScreen], animated: false)
self.mainWindow?.viewController = navigationController
navigationController.presentOverlay(controller: standardTextAlertController(theme: AlertControllerTheme(presentationTheme: defaultPresentationTheme), title: "Test", text: "Text", actions: [TextAlertAction(type: .defaultAction, title: "OK", action: {
})]), inGlobal: false)
self.window?.makeKeyAndVisible()

View File

@ -0,0 +1,193 @@
"Wallet.Updated.JustNow" = "just now";
"Wallet.Updated.MinutesAgo_0" = "%@ minutes ago"; //three to ten
"Wallet.Updated.MinutesAgo_1" = "1 minute ago"; //one
"Wallet.Updated.MinutesAgo_2" = "2 minutes ago"; //two
"Wallet.Updated.MinutesAgo_3_10" = "%@ minutes ago"; //three to ten
"Wallet.Updated.MinutesAgo_many" = "%@ minutes ago"; // more than ten
"Wallet.Updated.MinutesAgo_any" = "%@ minutes ago"; // more than ten
"Wallet.Updated.HoursAgo_0" = "%@ hours ago";
"Wallet.Updated.HoursAgo_1" = "1 hour ago";
"Wallet.Updated.HoursAgo_2" = "2 hours ago";
"Wallet.Updated.HoursAgo_3_10" = "%@ hours ago";
"Wallet.Updated.HoursAgo_any" = "%@ hours ago";
"Wallet.Updated.HoursAgo_many" = "%@ hours ago";
"Wallet.Updated.HoursAgo_0" = "%@ hours ago";
"Wallet.Updated.YesterdayAt" = "yesterday at %@";
"Wallet.Updated.AtDate" = "%@";
"Wallet.Updated.TodayAt" = "today at %@";
"Wallet.Info.WalletCreated" = "Wallet Created";
"Wallet.Info.Address" = "Your wallet address";
"Wallet.Info.YourBalance" = "your balance";
"Wallet.Info.Receive" = "Receive";
"Wallet.Info.Send" = "Send";
"Wallet.Info.RefreshErrorTitle" = "No network";
"Wallet.Info.RefreshErrorText" = "Couldn't refresh balance. Please make sure your internet connection is working and try again.";
"Wallet.Info.RefreshErrorNetworkText" = "The wallet state can not be retrieved at this time. Please try again later.";
"Wallet.Info.UnknownTransaction" = "Empty Transaction";
"Wallet.Info.TransactionTo" = "to";
"Wallet.Info.TransactionFrom" = "from";
"Wallet.Info.Updating" = "updating";
"Wallet.Info.TransactionBlockchainFee" = "%@ blockchain fee";
"Wallet.Info.TransactionPendingHeader" = "Pending";
"Wallet.Qr.ScanCode" = "Scan QR Code";
"Wallet.Qr.Title" = "QR Code";
"Wallet.Receive.Title" = "Receive Grams";
"Wallet.Receive.AddressHeader" = "YOUR WALLET ADDRESS";
"Wallet.Receive.InvoiceUrlHeader" = "INVOICE URL";
"Wallet.Receive.CopyAddress" = "Copy Wallet Address";
"Wallet.Receive.CopyInvoiceUrl" = "Copy Invoice URL";
"Wallet.Receive.ShareAddress" = "Share Wallet Address";
"Wallet.Receive.ShareInvoiceUrl" = "Share Invoice URL";
"Wallet.Receive.ShareUrlInfo" = "Share this link with other Gram wallet owners to receive Grams from them.";
"Wallet.Receive.AmountHeader" = "AMOUNT";
"Wallet.Receive.AmountText" = "Grams to receive";
"Wallet.Receive.AmountInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
"Wallet.Receive.CommentHeader" = "COMMENT (OPTIONAL)";
"Wallet.Receive.CommentInfo" = "Description of the payment";
"Wallet.Receive.AddressCopied" = "Address copied to clipboard.";
"Wallet.Receive.InvoiceUrlCopied" = "Invoice URL copied to clipboard.";
"Wallet.Send.Title" = "Send Grams";
"Wallet.Send.AddressHeader" = "RECIPIENT WALLET ADDRESS";
"Wallet.Send.AddressText" = "Enter wallet address...";
"Wallet.Send.AddressInfo" = "Paste the 48-letter address of the recipient here or ask them to send you a ton:// link.";
"Wallet.Send.Balance" = "Balance: %@";
"Wallet.Send.AmountText" = "Grams to send";
"Wallet.Send.Confirmation" = "Confirmation";
"Wallet.Send.ConfirmationText" = "Do you want to send **%1$@** Grams to\n%2$@?";
"Wallet.Send.ConfirmationConfirm" = "Confirm";
"Wallet.Send.Send" = "Send";
"Wallet.Settings.Title" = "Wallet Settings";
"Wallet.Settings.DeleteWallet" = "Delete Wallet";
"Wallet.Settings.DeleteWalletInfo" = "This will disconnect the wallet from this app. You will be able to restore your wallet using 24 secret words or import another wallet.\n\nWallets are located in the TON Blockchain, which is not controlled by Telegram. If you want a wallet to be deleted, simply transfer all the grams from it and leave it empty.";
"Wallet.Intro.NotNow" = "Not Now";
"Wallet.Intro.ImportExisting" = "Import existing wallet";
"Wallet.Intro.CreateErrorTitle" = "An Error Occurred";
"Wallet.Intro.CreateErrorText" = "Sorry. Please try again.";
"Wallet.Intro.Title" = "Gram Wallet";
"Wallet.Intro.Text" = "The gram wallet allows you to make fast and secure blockchain-based payments without intermediaries.";
"Wallet.Intro.CreateWallet" = "Create My Wallet";
"Wallet.Intro.Terms" = "By creating a wallet you accept the\n[Terms of Conditions]().";
"Wallet.Intro.TermsUrl" = "https://telegram.org/tos/wallet";
"Wallet.Created.Title" = "Congratulations";
"Wallet.Created.Text" = "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode.";
"Wallet.Created.Proceed" = "Proceed";
"Wallet.Created.ExportErrorTitle" = "Error";
"Wallet.Created.ExportErrorText" = "Encryption error. Please make sure you have enabled a device passcode in iOS settings and try again.";
"Wallet.Completed.Title" = "Ready to go!";
"Wallet.Completed.Text" = "Youre all set. Now you have a wallet that only you control - directly, without middlemen or bankers.";
"Wallet.Completed.ViewWallet" = "View My Wallet";
"Wallet.RestoreFailed.Title" = "Too Bad";
"Wallet.RestoreFailed.Text" = "Without the secret words, you can't\nrestore access to your wallet.";
"Wallet.RestoreFailed.CreateWallet" = "Create a New Wallet";
"Wallet.RestoreFailed.EnterWords" = "Enter 24 words";
"Wallet.Sending.Title" = "Sending Grams";
"Wallet.Sending.Text" = "Please wait a few seconds for your transaction to be processed...";
"Wallet.Sending.Title" = "Sending Grams";
"Wallet.Sent.Title" = "Done!";
"Wallet.Sent.Text" = "**%@ Grams** have been sent.";
"Wallet.Sent.ViewWallet" = "View My Wallet";
"Wallet.SecureStorageNotAvailable.Title" = "Set a Passcode";
"Wallet.SecureStorageNotAvailable.Text" = "Please set up a Passcode on your device to enable secure payments with your Gram wallet.";
"Wallet.SecureStorageReset.Title" = "Security Settings Have Changed";
"Wallet.SecureStorageReset.BiometryTouchId" = "Touch ID";
"Wallet.SecureStorageReset.BiometryFaceId" = "Face ID";
"Wallet.SecureStorageReset.BiometryText" = "Unfortunately, your wallet is no longer available because your system Passcode or %@ has been turned off. Please enable them before proceeding.";
"Wallet.SecureStorageReset.PasscodeText" = "Unfortunately, your wallet is no longer available because your system Passcode has been turned off. Please enable it before proceeding.";
"Wallet.SecureStorageChanged.BiometryText" = "Unfortunately, your wallet is no longer available due to the change in your system security settings (Passcode/%@). To restore your wallet, tap \"Import existing wallet\".";
"Wallet.SecureStorageChanged.PasscodeText" = "Unfortunately, your wallet is no longer available due to the change in your system security settings (Passcode). To restore your wallet, tap \"Import existing wallet\".";
"Wallet.SecureStorageChanged.ImportWallet" = "Import Existing Wallet";
"Wallet.SecureStorageChanged.CreateWallet" = "Create New Wallet";
"Wallet.TransactionInfo.Title" = "Transaction";
"Wallet.TransactionInfo.NoAddress" = "No Address";
"Wallet.TransactionInfo.RecipientHeader" = "RECIPIENT";
"Wallet.TransactionInfo.SenderHeader" = "SENDER";
"Wallet.TransactionInfo.CopyAddress" = "Copy Wallet Address";
"Wallet.TransactionInfo.AddressCopied" = "Address copied to clipboard.";
"Wallet.TransactionInfo.SendGrams" = "Send Grams";
"Wallet.TransactionInfo.CommentHeader" = "COMMENT";
"Wallet.TransactionInfo.StorageFeeHeader" = "STORAGE FEE";
"Wallet.TransactionInfo.OtherFeeHeader" = "TRANSACTION FEE";
"Wallet.TransactionInfo.StorageFeeInfo" = "Blockchain validators collect a tiny fee for storing information about your decentralized wallet. [More info]()";
"Wallet.TransactionInfo.OtherFeeInfo" = "Blockchain validators collect a tiny fee for processing your decentralized transactions. [More info]()";
"Wallet.TransactionInfo.FeeInfoURL" = "https://telegram.org/wallet/fee";
"Wallet.WordCheck.Title" = "Test Time!";
"Wallet.WordCheck.Text" = "Lets check that you wrote them down correctly. Please enter words\n**%1$@**, **%2$@** and **%3$@** below:";
"Wallet.WordCheck.Continue" = "Continue";
"Wallet.WordCheck.IncorrectHeader" = "Incorrect words!";
"Wallet.WordCheck.IncorrectText" = "The secret words you have entered do not match the ones in the list.";
"Wallet.WordCheck.TryAgain" = "Try Again";
"Wallet.WordCheck.ViewWords" = "View Words";
"Wallet.WordImport.Title" = "24 Secret Words";
"Wallet.WordImport.Text" = "Please restore access to your wallet by\nentering the 24 secret words you wrote down when creating the wallet.";
"Wallet.WordImport.Continue" = "Continue";
"Wallet.WordImport.CanNotRemember" = "I don't have them";
"Wallet.WordImport.IncorrectTitle" = "Incorrect words";
"Wallet.WordImport.IncorrectText" = "Sorry, you have entered incorrect secret words. Please double check and try again.";
"Wallet.Words.Title" = "24 Secret Words";
"Wallet.Words.Text" = "Write down these 24 words in the correct order and store them in a secret place.\n\nUse these secret words to restore access to your wallet if you lose your passcode or Telegram account.";
"Wallet.Words.Done" = "Done";
"Wallet.Words.NotDoneTitle" = "Sure Done?";
"Wallet.Words.NotDoneText" = "You didn't have enough time to write those words down.";
"Wallet.Words.NotDoneOk" = "OK, Sorry";
"Wallet.Words.NotDoneResponse" = "Apologies Accepted";
"Wallet.Send.NetworkErrorTitle" = "No network";
"Wallet.Send.NetworkErrorText" = "Couldn't send grams. Please make sure your internet connection is working and try again.";
"Wallet.Send.ErrorNotEnoughFundsTitle" = "Insufficient Grams";
"Wallet.Send.ErrorNotEnoughFundsText" = "Unfortunately, your transfer couldn't be completed. You don't have enough grams.";
"Wallet.Send.ErrorInvalidAddress" = "Invalid wallet address. Please correct and try again.";
"Wallet.Send.ErrorDecryptionFailed" = "Please make sure that your device has a passcode set in iOS Settings and try again.";
"Wallet.Send.UninitializedTitle" = "Warning";
"Wallet.Send.UninitializedText" = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?";
"Wallet.Send.SendAnyway" = "Send Anyway";
"Wallet.Receive.CreateInvoice" = "Create Invoice";
"Wallet.Receive.CreateInvoiceInfo" = "You can specify amount and purpose of the payment to save the sender some time.";
"Wallet.CreateInvoice.Title" = "Create Invoice";
"Wallet.Navigation.Close" = "Close";
"Wallet.Navigation.Back" = "Back";
"Wallet.Navigation.Done" = "Done";
"Wallet.Navigation.Cancel" = "Cancel";
"Wallet.Alert.OK" = "OK";
"Wallet.Alert.Cancel" = "Cancel";
"Wallet.Month.GenJanuary" = "January";
"Wallet.Month.GenFebruary" = "February";
"Wallet.Month.GenMarch" = "March";
"Wallet.Month.GenApril" = "April";
"Wallet.Month.GenMay" = "May";
"Wallet.Month.GenJune" = "June";
"Wallet.Month.GenJuly" = "July";
"Wallet.Month.GenAugust" = "August";
"Wallet.Month.GenSeptember" = "September";
"Wallet.Month.GenOctober" = "October";
"Wallet.Month.GenNovember" = "November";
"Wallet.Month.GenDecember" = "December";
"Wallet.Month.ShortJanuary" = "Jan";
"Wallet.Month.ShortFebruary" = "Feb";
"Wallet.Month.ShortMarch" = "Mar";
"Wallet.Month.ShortApril" = "Apr";
"Wallet.Month.ShortMay" = "May";
"Wallet.Month.ShortJune" = "Jun";
"Wallet.Month.ShortJuly" = "Jul";
"Wallet.Month.ShortAugust" = "Aug";
"Wallet.Month.ShortSeptember" = "Sep";
"Wallet.Month.ShortOctober" = "Oct";
"Wallet.Month.ShortNovember" = "Nov";
"Wallet.Month.ShortDecember" = "Dec";
"Wallet.Weekday.Today" = "Today";
"Wallet.Weekday.Yesterday" = "Yesterday";
"Wallet.Info.TransactionDateHeader" = "%1$@ %2$@";
"Wallet.Info.TransactionDateHeaderYear" = "%1$@ %2$@, %3$@";
"Wallet.UnknownError" = "An error occurred. Please try again later.";
"Wallet.ContextMenuCopy" = "Copy";
"Wallet.Time.PreciseDate_m1" = "Jan %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m2" = "Feb %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m3" = "Mar %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m4" = "Apr %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m5" = "May %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m6" = "Jun %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m7" = "Jul %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m8" = "Aug %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m9" = "Sep %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m10" = "Oct %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m11" = "Nov %1$@, %2$@ at %3$@";
"Wallet.Time.PreciseDate_m12" = "Dec %1$@, %2$@ at %3$@";
"Wallet.VoiceOver.Editing.ClearText" = "Clear text";

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
</dict>
</plist>

View File

@ -15,6 +15,7 @@ static_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#dynamic",
"//submodules/Postbox:Postbox#dynamic",
"//submodules/TelegramCore:TelegramCore#dynamic",
"//submodules/WalletCore:WalletCore",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -7,6 +7,7 @@ import SwiftSignalKit
import Display
import DeviceLocationManager
import TemporaryCachedPeerDataManager
import WalletCore
public final class TelegramApplicationOpenUrlCompletion {
public let completion: (Bool) -> Void
@ -457,6 +458,26 @@ private final class TonInstanceData {
var instance: TonInstance?
}
private final class TonNetworkProxyImpl: TonNetworkProxy {
private let network: Network
init(network: Network) {
self.network = network
}
func request(data: Data, timeout timeoutValue: Double, completion: @escaping (TonNetworkProxyResult) -> Void) -> Disposable {
return (walletProxyRequest(network: self.network, data: data)
|> timeout(timeoutValue, queue: .concurrentDefaultQueue(), alternate: .fail(.generic(500, "Local Timeout")))).start(next: { data in
completion(.reponse(data))
}, error: { error in
switch error {
case let .generic(_, text):
completion(.error(text))
}
})
}
}
public final class StoredTonContext {
private let basePath: String
private let postbox: Postbox
@ -477,7 +498,7 @@ public final class StoredTonContext {
return TonContext(instance: instance, keychain: self.keychain)
} else {
data.config = config
let instance = TonInstance(basePath: self.basePath, config: config, blockchainName: blockchainName, network: enableProxy ? self.network : nil)
let instance = TonInstance(basePath: self.basePath, config: config, blockchainName: blockchainName, proxy: enableProxy ? TonNetworkProxyImpl(network: self.network) : nil)
data.instance = instance
return TonContext(instance: instance, keychain: self.keychain)
}
@ -506,6 +527,7 @@ public protocol AccountContext: class {
var peerChannelMemberCategoriesContextsManager: PeerChannelMemberCategoriesContextsManager { get }
var wallpaperUploadManager: WallpaperUploadManager? { get }
var watchManager: WatchManager? { get }
var hasWallets: Signal<Bool, NoError> { get }
var currentLimitsConfiguration: Atomic<LimitsConfiguration> { get }

View File

@ -7,7 +7,7 @@ static_library(
]),
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/Display:Display#shared",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -1,7 +1,19 @@
import Foundation
import UIKit
import AsyncDisplayKit
import TelegramPresentationData
import Display
private func generateIndefiniteActivityIndicatorImage(color: UIColor, diameter: CGFloat = 22.0, lineWidth: CGFloat = 2.0) -> UIImage? {
return generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setStrokeColor(color.cgColor)
context.setLineWidth(lineWidth)
context.setLineCap(.round)
let cutoutAngle: CGFloat = CGFloat.pi * 30.0 / 180.0
context.addArc(center: CGPoint(x: size.width / 2.0, y: size.height / 2.0), radius: size.width / 2.0 - lineWidth / 2.0, startAngle: 0.0, endAngle: CGFloat.pi * 2.0 - cutoutAngle, clockwise: false)
context.strokePath()
})
}
private func convertIndicatorColor(_ color: UIColor) -> UIColor {
if color.isEqual(UIColor(rgb: 0x007ee5)) {
@ -16,13 +28,13 @@ private func convertIndicatorColor(_ color: UIColor) -> UIColor {
}
public enum ActivityIndicatorType: Equatable {
case navigationAccent(PresentationTheme)
case navigationAccent(UIColor)
case custom(UIColor, CGFloat, CGFloat, Bool)
public static func ==(lhs: ActivityIndicatorType, rhs: ActivityIndicatorType) -> Bool {
switch lhs {
case let .navigationAccent(lhsTheme):
if case let .navigationAccent(rhsTheme) = rhs, lhsTheme === rhsTheme {
case let .navigationAccent(lhsColor):
if case let .navigationAccent(rhsColor) = rhs, lhsColor.isEqual(rhsColor) {
return true
} else {
return false
@ -46,15 +58,15 @@ public final class ActivityIndicator: ASDisplayNode {
public var type: ActivityIndicatorType {
didSet {
switch self.type {
case let .navigationAccent(theme):
self.indicatorNode.image = PresentationResourcesRootController.navigationIndefiniteActivityImage(theme)
case let .navigationAccent(color):
self.indicatorNode.image = generateIndefiniteActivityIndicatorImage(color: color)
case let .custom(color, diameter, lineWidth, _):
self.indicatorNode.image = generateIndefiniteActivityIndicatorImage(color: color, diameter: diameter, lineWidth: lineWidth)
}
switch self.type {
case let .navigationAccent(theme):
self.indicatorView?.color = theme.rootController.navigationBar.controlColor
case let .navigationAccent(color):
self.indicatorView?.color = color
case let .custom(color, _, _, _):
self.indicatorView?.color = convertIndicatorColor(color)
}
@ -90,8 +102,8 @@ public final class ActivityIndicator: ASDisplayNode {
}
switch type {
case let .navigationAccent(theme):
self.indicatorNode.image = PresentationResourcesRootController.navigationIndefiniteActivityImage(theme)
case let .navigationAccent(color):
self.indicatorNode.image = generateIndefiniteActivityIndicatorImage(color: color)
case let .custom(color, diameter, lineWidth, forceCustom):
self.indicatorNode.image = generateIndefiniteActivityIndicatorImage(color: color, diameter: diameter, lineWidth: lineWidth)
if forceCustom {
@ -105,8 +117,8 @@ public final class ActivityIndicator: ASDisplayNode {
let indicatorView = UIActivityIndicatorView(style: .whiteLarge)
switch self.type {
case let .navigationAccent(theme):
indicatorView.color = theme.rootController.navigationBar.controlColor
case let .navigationAccent(color):
indicatorView.color = color
case let .custom(color, _, _, forceCustom):
indicatorView.color = convertIndicatorColor(color)
if !forceCustom {

View File

@ -0,0 +1,20 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "AnimatedStickerNode",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
"//submodules/YuvConversion:YuvConversion",
"//submodules/GZip:GZip",
"//submodules/rlottie:RLottieBinding",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)

View File

@ -1,15 +1,10 @@
import Foundation
import SwiftSignalKit
import Postbox
import TelegramCore
import Compression
import Display
import AsyncDisplayKit
import RLottieBinding
import GZip
import Tuples
import MediaResources
import StickerResources
private let sharedQueue = Queue()
@ -301,14 +296,29 @@ public struct AnimatedStickerStatus: Equatable {
}
}
public enum AnimatedStickerNodeResource {
case resource(Account, MediaResource)
case localFile(String)
public protocol AnimatedStickerNodeSource {
func cachedDataPath(width: Int, height: Int) -> Signal<String, NoError>
func directDataPath() -> Signal<String, NoError>
}
public final class AnimatedStickerNodeLocalFileSource: AnimatedStickerNodeSource {
public let path: String
public init(path: String) {
self.path = path
}
public func directDataPath() -> Signal<String, NoError> {
return .single(self.path)
}
public func cachedDataPath(width: Int, height: Int) -> Signal<String, NoError> {
return .never()
}
}
public final class AnimatedStickerNode: ASDisplayNode {
private let queue: Queue
private var fileReference: FileMediaReference?
private let disposable = MetaDisposable()
private let fetchDisposable = MetaDisposable()
private let eventsNode: AnimatedStickerNodeDisplayEvents
@ -321,7 +331,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
private let timer = Atomic<SwiftSignalKit.Timer?>(value: nil)
private var directData: Tuple4<Data, String, Int, Int>?
private var directData: (Data, String, Int, Int)?
private var cachedData: Data?
private var renderer: (AnimationRenderer & ASDisplayNode)?
@ -385,19 +395,19 @@ public final class AnimatedStickerNode: ASDisplayNode {
self.addSubnode(self.renderer!)
}
public func setup(resource: AnimatedStickerNodeResource, fitzModifier: EmojiFitzModifier? = nil, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
public func setup(source: AnimatedStickerNodeSource, width: Int, height: Int, playbackMode: AnimatedStickerPlaybackMode = .loop, mode: AnimatedStickerMode) {
if width < 2 || height < 2 {
return
}
self.playbackMode = playbackMode
switch mode {
case .direct:
let f: (MediaResourceData) -> Void = { [weak self] data in
guard let strongSelf = self, data.complete else {
let f: (String) -> Void = { [weak self] path in
guard let strongSelf = self else {
return
}
if let directData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead]) {
strongSelf.directData = Tuple(directData, data.path, width, height)
if let directData = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
strongSelf.directData = (directData, path, width, height)
}
if strongSelf.isPlaying {
strongSelf.play()
@ -405,32 +415,23 @@ public final class AnimatedStickerNode: ASDisplayNode {
strongSelf.play(firstFrame: true)
}
}
switch resource {
case let .resource(account, resource):
self.disposable.set((account.postbox.mediaBox.resourceData(resource)
|> deliverOnMainQueue).start(next: { data in
f(data)
}))
case let .localFile(path):
f(MediaResourceData(path: path, offset: 0, size: Int(Int32.max - 1), complete: true))
}
self.disposable.set((source.directDataPath()
|> deliverOnMainQueue).start(next: { path in
f(path)
}))
case .cached:
switch resource {
case let .resource(account, resource):
self.disposable.set((chatMessageAnimationData(postbox: account.postbox, resource: resource, fitzModifier: fitzModifier, width: width, height: height, synchronousLoad: false)
|> deliverOnMainQueue).start(next: { [weak self] data in
if let strongSelf = self, data.complete {
strongSelf.cachedData = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [.mappedRead])
if strongSelf.isPlaying {
strongSelf.play()
} else if strongSelf.canDisplayFirstFrame {
strongSelf.play(firstFrame: true)
}
}
}))
case .localFile:
break
}
self.disposable.set((source.cachedDataPath(width: width, height: height)
|> deliverOnMainQueue).start(next: { [weak self] path in
guard let strongSelf = self else {
return
}
strongSelf.cachedData = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead])
if strongSelf.isPlaying {
strongSelf.play()
} else if strongSelf.canDisplayFirstFrame {
strongSelf.play(firstFrame: true)
}
}))
}
}
@ -466,7 +467,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
self.queue.async { [weak self] in
var maybeFrameSource: AnimatedStickerFrameSource?
if let directData = directData {
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData._0, width: directData._2, height: directData._3)
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
} else if let cachedData = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData)
@ -539,7 +540,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
self.queue.async { [weak self] in
var maybeFrameSource: AnimatedStickerFrameSource?
if let directData = directData {
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData._0, width: directData._2, height: directData._3)
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3)
} else if let cachedData = cachedData {
if #available(iOS 9.0, *) {
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData)

View File

@ -1,19 +0,0 @@
//
// AnimationUI.h
// AnimationUI
//
// Created by Peter on 8/1/19.
// Copyright © 2019 Telegram Messenger LLP. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for AnimationUI.
FOUNDATION_EXPORT double AnimationUIVersionNumber;
//! Project version string for AnimationUI.
FOUNDATION_EXPORT const unsigned char AnimationUIVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <AnimationUI/PublicHeader.h>

View File

@ -10,6 +10,7 @@ static_library(
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/Display:Display#shared",
"//submodules/TextFormat:TextFormat",
"//submodules/Markdown:Markdown",
"//submodules/TelegramPresentationData:TelegramPresentationData",
],
frameworks = [

View File

@ -3,6 +3,7 @@ import TelegramCore
import Display
import TelegramPresentationData
import TextFormat
import Markdown
public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, strings: PresentationStrings, primaryColor: UIColor, accentColor: UIColor) -> NSAttributedString {
switch type {

View File

@ -19,7 +19,7 @@ static_library(
"//submodules/Stripe:Stripe",
"//submodules/CountrySelectionUI:CountrySelectionUI",
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -99,7 +99,7 @@ final class BotCheckoutInfoController: ViewController {
switch status {
case .verifying:
if strongSelf.activityItem == nil {
strongSelf.activityItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
strongSelf.activityItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
strongSelf.navigationItem.setRightBarButton(strongSelf.activityItem, animated: false)
}
default:

View File

@ -97,7 +97,7 @@ final class BotCheckoutNativeCardEntryController: ViewController {
switch status {
case .verifying:
if strongSelf.activityItem == nil {
strongSelf.activityItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
strongSelf.activityItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
strongSelf.navigationItem.setRightBarButton(strongSelf.activityItem, animated: false)
}
default:

View File

@ -22,6 +22,7 @@ static_library(
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/ListSectionHeaderNode:ListSectionHeaderNode",
"//submodules/ContextUI:ContextUI",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -14,6 +14,7 @@ static_library(
"//submodules/AccountContext:AccountContext",
"//submodules/TextFormat:TextFormat",
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -18,6 +18,7 @@ static_library(
"//submodules/ActivityIndicator:ActivityIndicator",
"//submodules/ItemListUI:ItemListUI",
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -11,6 +11,7 @@ static_library(
"//submodules/Display:Display#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ItemListUI:ItemListUI",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -18,6 +18,7 @@ static_library(
"//submodules/ItemListUI:ItemListUI",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/ContextUI:ContextUI",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -14,7 +14,9 @@ static_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ItemListUI:ItemListUI",
"//submodules/StickerResources:StickerResources",
"//submodules/AnimationUI:AnimationUI",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -9,7 +9,8 @@ import TelegramPresentationData
import ItemListUI
import PresentationDataUtils
import StickerResources
import AnimationUI
import AnimatedStickerNode
import TelegramAnimatedStickerNode
public struct ItemListStickerPackItemEditing: Equatable {
public var editable: Bool
@ -600,7 +601,7 @@ class ItemListStickerPackItemNode: ItemListRevealOptionsItemNode {
animationNode = AnimatedStickerNode()
strongSelf.animationNode = animationNode
strongSelf.addSubnode(animationNode)
animationNode.setup(resource: .resource(item.account, resource), width: 80, height: 80, mode: .cached)
animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: resource), width: 80, height: 80, mode: .cached)
}
animationNode.visibility = strongSelf.visibility != .none && item.playAnimatedStickers
animationNode.isHidden = !item.playAnimatedStickers

View File

@ -12,11 +12,14 @@ static_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/MergeLists:MergeLists",
"//submodules/TextFormat:TextFormat",
"//submodules/Markdown:Markdown",
"//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode",
"//submodules/SwitchNode:SwitchNode",
"//submodules/AnimationUI:AnimationUI",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/CheckNode:CheckNode",
"//submodules/SegmentedControlNode:SegmentedControlNode",
"//submodules/AccountContext:AccountContext",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -350,7 +350,7 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
for (content, style, _) in rightNavigationButtonTitleAndStyle {
let item: UIBarButtonItem
if case .activity = style {
item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: controllerState.theme))
item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: controllerState.theme.rootController.navigationBar.controlColor))
} else {
let action: Selector = (index == 0 && rightNavigationButtonTitleAndStyle.count > 1) ? #selector(strongSelf.secondaryRightNavigationButtonPressed) : #selector(strongSelf.rightNavigationButtonPressed)
switch content {
@ -407,7 +407,7 @@ open class ItemListController: ViewController, KeyShortcutResponder, Presentable
var items = strongSelf.navigationItem.rightBarButtonItems ?? []
for i in 0 ..< strongSelf.rightNavigationButtonTitleAndStyle.count {
if case .activity = strongSelf.rightNavigationButtonTitleAndStyle[i].1 {
items[i] = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: controllerState.theme))!
items[i] = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: controllerState.theme.rootController.navigationBar.controlColor))!
}
}
strongSelf.navigationItem.setRightBarButtonItems(items, animated: false)

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TextFormat
import Markdown
public enum InfoListItemText {
case plain(String)

View File

@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TextFormat
import Markdown
public enum ItemListTextItemText {
case plain(String)

View File

@ -14,6 +14,7 @@ static_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",
"//submodules/TextFormat:TextFormat",
"//submodules/Markdown:Markdown",
"//submodules/ShareController:ShareController",
"//submodules/AlertUI:AlertUI",
"//submodules/PresentationDataUtils:PresentationDataUtils",

View File

@ -8,6 +8,7 @@ import TelegramPresentationData
import TextFormat
import AccountContext
import ShareController
import Markdown
final class LanguageLinkPreviewContentNode: ASDisplayNode, ShareContentContainerNode {
private var contentOffsetUpdated: ((CGFloat, ContainedViewLayoutTransition) -> Void)?

14
submodules/Markdown/BUCK Normal file
View File

@ -0,0 +1,14 @@
load("//Config:buck_rule_macros.bzl", "static_library")
static_library(
name = "Markdown",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
],
)

View File

@ -1,6 +1,5 @@
import Foundation
import UIKit
import Display
private let controlStartCharactersSet = CharacterSet(charactersIn: "[*")
private let controlCharactersSet = CharacterSet(charactersIn: "[]()*_-\\")

View File

@ -14,6 +14,7 @@ static_library(
"//submodules/ItemListUI:ItemListUI",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -1,13 +1,27 @@
load("//Config:buck_rule_macros.bzl", "static_library")
apple_resource(
name = "OverlayStatusControllerResources",
files = glob([
"Resources/**/*",
], exclude = ["Resources/**/.*"]),
visibility = ["PUBLIC"],
)
static_library(
name = "OverlayStatusController",
srcs = glob([
"Sources/**/*.swift",
"Sources/**/*.m",
]),
headers = glob([
"Sources/**/*.h",
]),
exported_headers = glob([
"Sources/**/*.h",
]),
deps = [
"//submodules/Display:Display#shared",
"//submodules/LegacyComponents:LegacyComponents",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -1,19 +0,0 @@
//
// OverlayStatusController.h
// OverlayStatusController
//
// Created by Peter on 8/4/19.
// Copyright © 2019 Telegram Messenger LLP. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for OverlayStatusController.
FOUNDATION_EXPORT double OverlayStatusControllerVersionNumber;
//! Project version string for OverlayStatusController.
FOUNDATION_EXPORT const unsigned char OverlayStatusControllerVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <OverlayStatusController/PublicHeader.h>

View File

@ -1,7 +1,7 @@
import Foundation
import UIKit
import Display
import LegacyComponents
import AppBundle
public enum OverlayStatusControllerType {
case loading(cancelled: (() -> Void)?)
@ -12,11 +12,11 @@ public enum OverlayStatusControllerType {
}
private enum OverlayStatusContentController {
case loading(TGProgressWindowController)
case progress(TGProgressWindowController)
case shieldSuccess(TGProxyWindowController, Bool)
case genericSuccess(TGProxyWindowController, Bool)
case starSuccess(TGProxyWindowController)
case loading(ProgressWindowController)
case progress(ProgressWindowController)
case shieldSuccess(ProxyWindowController, Bool)
case genericSuccess(ProxyWindowController, Bool)
case starSuccess(ProxyWindowController)
var view: UIView {
switch self {
@ -84,21 +84,21 @@ private final class OverlayStatusControllerNode: ViewControllerTracingNode {
var isUserInteractionEnabled = true
switch type {
case let .loading(cancelled):
let controller = TGProgressWindowController(light: style == .light)!
let controller = ProgressWindowController(light: style == .light)!
controller.cancelled = {
cancelled?()
}
self.contentController = .loading(controller)
case .success:
self.contentController = .progress(TGProgressWindowController(light: style == .light))
self.contentController = .progress(ProgressWindowController(light: style == .light))
case let .shieldSuccess(text, increasedDelay):
self.contentController = .shieldSuccess(TGProxyWindowController(light: style == .light, text: text, shield: true, star: false), increasedDelay)
self.contentController = .shieldSuccess(ProxyWindowController(light: style == .light, text: text, icon: ProxyWindowController.generateShieldImage(style == .light), isShield: true), increasedDelay)
case let .genericSuccess(text, increasedDelay):
let controller = TGProxyWindowController(light: style == .light, text: text, shield: false, star: false)!
let controller = ProxyWindowController(light: style == .light, text: text, icon: nil, isShield: false)!
self.contentController = .genericSuccess(controller, increasedDelay)
isUserInteractionEnabled = false
case let .starSuccess(text):
self.contentController = .genericSuccess(TGProxyWindowController(light: style == .light, text: text, shield: false, star: true), false)
self.contentController = .genericSuccess(ProxyWindowController(light: style == .light, text: text, icon: UIImage(bundleImageName: "Star"), isShield: false), false)
}
super.init()

View File

@ -0,0 +1,12 @@
#import <UIKit/UIKit.h>
@interface ProgressSpinnerView : UIView
@property (nonatomic, copy) void (^onSuccess)(void);
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
- (void)setProgress;
- (void)setSucceed;
@end

View File

@ -0,0 +1,305 @@
#import "ProgressSpinnerView.h"
#define UIColorRGB(rgb) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:1.0f])
@interface ProgressSpinnerViewInternal : UIView
@property (nonatomic, copy) void (^onDraw)(void);
@property (nonatomic, copy) void (^onSuccess)(void);
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
- (void)setProgress;
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress;
@end
@interface ProgressSpinnerView ()
{
UIImageView *_arcView;
ProgressSpinnerViewInternal *_internalView;
bool _progressing;
}
@end
@implementation ProgressSpinnerView
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
self = [super initWithFrame:frame];
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.opaque = false;
self.userInteractionEnabled = false;
UIImage *arcImage = nil;
CGRect rect = CGRectMake(0.0f, 0.0f, frame.size.width, frame.size.height);
UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
CGFloat lineWidth = 4.0f;
CGFloat inset = 3.0f;
UIColor *foregroundColor = light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
CGContextSetFillColorWithColor(context, foregroundColor.CGColor);
CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor);
CGMutablePathRef path = CGPathCreateMutable();
CGFloat offset = -2.0f * M_PI;
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2), false);
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10);
CGContextAddPath(context, strokedArc);
CGPathRelease(strokedArc);
CGPathRelease(path);
CGContextFillPath(context);
arcImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//});
_arcView = [[UIImageView alloc] initWithFrame:self.bounds];
_arcView.image = arcImage;
_arcView.hidden = true;
[self addSubview:_arcView];
_internalView = [[ProgressSpinnerViewInternal alloc] initWithFrame:self.bounds light:light];
_internalView.hidden = true;
[self addSubview:_internalView];
}
return self;
}
- (void)setProgress {
_arcView.hidden = false;
_progressing = true;
CABasicAnimation *rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = @(-M_PI * 2.0f);
rotationAnimation.duration = 0.75;
rotationAnimation.cumulative = true;
rotationAnimation.repeatCount = HUGE_VALF;
[_arcView.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
- (void)setSucceed {
_internalView.hidden = false;
if (_progressing)
{
CGFloat value = [[_arcView.layer.presentationLayer valueForKeyPath:@"transform.rotation.z"] doubleValue] / (-2 * M_PI);
[_internalView setSucceed:_progressing progress:value];
__weak ProgressSpinnerView *weakSelf = self;
_internalView.onDraw = ^{
__strong ProgressSpinnerView *strongSelf = weakSelf;
if (strongSelf != nil)
strongSelf->_arcView.hidden = true;
};
_internalView.onSuccess = ^{
__strong ProgressSpinnerView *strongSelf = weakSelf;
if (strongSelf != nil && strongSelf.onSuccess != nil)
strongSelf.onSuccess();
};
}
else
{
[_internalView setSucceed:false progress:0.0f];
}
}
@end
@interface ProgressSpinnerViewInternal ()
{
CADisplayLink *_displayLink;
bool _light;
bool _isProgressing;
CGFloat _rotationValue;
bool _isRotating;
CGFloat _checkValue;
bool _delay;
bool _isSucceed;
bool _isChecking;
NSTimeInterval _previousTime;
}
@end
@implementation ProgressSpinnerViewInternal
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
self = [super initWithFrame:frame];
if (self != nil) {
_light = light;
self.backgroundColor = [UIColor clearColor];
self.opaque = false;
self.userInteractionEnabled = false;
}
return self;
}
- (void)dealloc {
_displayLink.paused = true;
[_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (CADisplayLink *)displayLink {
if (_displayLink == nil) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkUpdate)];
_displayLink.paused = true;
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
return _displayLink;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
CGFloat lineWidth = 4.0f;
CGFloat inset = 3.0f;
if (rect.size.width < 44.0) {
inset = 0.0f;
}
UIColor *foregroundColor = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
CGContextSetFillColorWithColor(context, foregroundColor.CGColor);
CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor);
if (_isProgressing)
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat offset = -_rotationValue * 2.0f * M_PI;
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2) * (1.0f - _checkValue), false);
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10);
CGContextAddPath(context, strokedArc);
CGPathRelease(strokedArc);
CGPathRelease(path);
CGContextFillPath(context);
}
if (_checkValue > FLT_EPSILON)
{
CGContextSetLineWidth(context, 5.0f);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetMiterLimit(context, 10);
CGFloat firstSegment = MIN(1.0f, _checkValue * 3.0f);
CGPoint s = CGPointMake(inset + 5.0f / 2.0f, centerPoint.y);
CGPoint p1 = CGPointMake(13.0f, 13.0f);
CGPoint p2 = CGPointMake(27.0f, -27.0f);
if (firstSegment < 1.0f)
{
CGContextMoveToPoint(context, s.x + p1.x * firstSegment, s.y + p1.y * firstSegment);
CGContextAddLineToPoint(context, s.x, s.y);
}
else
{
CGFloat secondSegment = (_checkValue - 0.33f) * 1.5f;
CGContextMoveToPoint(context, s.x + p1.x + p2.x * secondSegment, s.y + p1.y + p2.y * secondSegment);
CGContextAddLineToPoint(context, s.x + p1.x, s.y + p1.y);
CGContextAddLineToPoint(context, s.x, s.y);
}
CGContextStrokePath(context);
}
}
- (void)displayLinkUpdate
{
NSTimeInterval previousTime = _previousTime;
NSTimeInterval currentTime = CACurrentMediaTime();
_previousTime = currentTime;
NSTimeInterval delta = previousTime > DBL_EPSILON ? currentTime - previousTime : 0.0;
if (delta < DBL_EPSILON)
return;
if (_isRotating)
{
_rotationValue += delta * 1.35f;
}
if (_isSucceed && _isRotating && !_delay && _rotationValue >= 0.5f)
{
_rotationValue = 0.5f;
_isRotating = false;
_isChecking = true;
}
if (_isChecking)
_checkValue += delta * M_PI * 1.6f;
if (_rotationValue > 1.0f)
{
_rotationValue = 0.0f;
_delay = false;
}
if (_checkValue > 1.0f)
{
_checkValue = 1.0f;
[self displayLink].paused = true;
if (self.onSuccess != nil)
{
void (^onSuccess)(void) = [self.onSuccess copy];
self.onSuccess = nil;
onSuccess();
}
}
[self setNeedsDisplay];
if (self.onDraw != nil)
{
void (^onDraw)(void) = [self.onDraw copy];
self.onDraw = nil;
onDraw();
}
}
- (void)setProgress {
_isRotating = true;
_isProgressing = true;
[self displayLink].paused = false;
}
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress {
if (_isSucceed)
return;
if (fromRotation) {
_isRotating = true;
_isProgressing = true;
_rotationValue = progress;
}
_isSucceed = true;
if (!_isRotating)
_isChecking = true;
else if (_rotationValue > 0.5f)
_delay = true;
[self displayLink].paused = false;
}
- (bool)isSucceed
{
return _isSucceed;
}
@end

View File

@ -0,0 +1,16 @@
#import <UIKit/UIKit.h>
@interface ProgressWindowController : UIViewController
@property (nonatomic, copy) void (^cancelled)(void);
- (instancetype)init;
- (instancetype)initWithLight:(bool)light;
- (void)show:(bool)animated;
- (void)dismiss:(bool)animated completion:(void (^)(void))completion;
- (void)dismissWithSuccess:(void (^)(void))completion;
- (void)updateLayout;
@end

View File

@ -0,0 +1,194 @@
#import "ProgressWindow.h"
#import "ProgressSpinnerView.h"
#define UIColorRGBA(rgb,a) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:a])
#ifdef __LP64__
# define CGFloor floor
#else
# define CGFloor floorf
#endif
static inline void dispatchAfter(double delay, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((delay) * NSEC_PER_SEC)), queue, block);
}
static bool ProgressWindowIsLight = true;
@interface ProgressWindowController ()
{
bool _light;
UIVisualEffectView *_effectView;
UIView *_backgroundView;
ProgressSpinnerView *_spinner;
}
@property (nonatomic, weak) UIWindow *weakWindow;
@property (nonatomic, strong) UIView *containerView;
@end
@implementation ProgressWindowController
- (instancetype)init {
return [self initWithLight:ProgressWindowIsLight];
}
- (instancetype)initWithLight:(bool)light
{
self = [super init];
if (self != nil)
{
_light = light;
}
return self;
}
- (void)loadView
{
[super loadView];
_containerView = [[UIView alloc] initWithFrame:CGRectMake(CGFloor(self.view.frame.size.width - 100) / 2, CGFloor(self.view.frame.size.height - 100) / 2, 100, 100)];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
_containerView.alpha = 0.0f;
_containerView.clipsToBounds = true;
_containerView.layer.cornerRadius = 20.0f;
_containerView.userInteractionEnabled = false;
[self.view addSubview:_containerView];
if ([[[UIDevice currentDevice] systemVersion] intValue] >= 9)
{
_effectView = [[UIVisualEffectView alloc] initWithEffect:_light ? [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight] : [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
_effectView.frame = _containerView.bounds;
[_containerView addSubview:_effectView];
if (_light)
{
UIView *tintView = [[UIView alloc] initWithFrame:_effectView.bounds];
tintView.backgroundColor = UIColorRGBA(0xf4f4f4, 0.75f);
[_containerView addSubview:tintView];
}
}
else
{
_backgroundView = [[UIView alloc] initWithFrame:_containerView.bounds];
_backgroundView.backgroundColor = _light ? UIColorRGBA(0xeaeaea, 0.92f) : UIColorRGBA(0x000000, 0.9f);
[_containerView addSubview:_backgroundView];
}
_spinner = [[ProgressSpinnerView alloc] initWithFrame:CGRectMake((_containerView.frame.size.width - 48.0f) / 2.0f, (_containerView.frame.size.height - 48.0f) / 2.0f, 48.0f, 48.0f) light:_light];
[_containerView addSubview:_spinner];
self.view.userInteractionEnabled = true;
[self.view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)]];
}
- (void)tapGesture:(UITapGestureRecognizer *)recognizer {
if (recognizer.state == UIGestureRecognizerStateEnded) {
if (_cancelled) {
_cancelled();
}
}
}
- (void)updateLayout {
_containerView.frame = CGRectMake(CGFloor(self.view.frame.size.width - 100) / 2, CGFloor(self.view.frame.size.height - 100) / 2, 100, 100);
_spinner.frame = CGRectMake((_containerView.frame.size.width - 48.0f) / 2.0f, (_containerView.frame.size.height - 48.0f) / 2.0f, 48.0f, 48.0f);
}
- (void)show:(bool)animated
{
UIWindow *window = _weakWindow;
window.userInteractionEnabled = true;
window.hidden = false;
[_spinner setProgress];
if (animated)
{
_containerView.transform = CGAffineTransformMakeScale(0.6f, 0.6f);
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{
_containerView.transform = CGAffineTransformIdentity;
} completion:nil];
[UIView animateWithDuration:0.3f animations:^
{
_containerView.alpha = 1.0f;
}];
}
else
_containerView.alpha = 1.0f;
}
- (void)dismiss:(bool)animated {
[self dismiss:animated completion:nil];
}
- (void)dismiss:(bool)animated completion:(void (^)())completion
{
if (animated)
{
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^
{
_containerView.alpha = 0.0f;
} completion:^(__unused BOOL finished)
{
if (completion) {
completion();
}
}];
}
else
{
_containerView.alpha = 0.0f;
if (completion) {
completion();
}
}
}
- (void)dismissWithSuccess:(void (^)(void))completion
{
void (^dismissBlock)(void) = ^
{
[UIView animateWithDuration:0.3 delay:0.55 options:0 animations:^
{
_containerView.alpha = 0.0f;
} completion:^(BOOL finished)
{
if (finished)
{
if (completion) {
completion();
}
}
}];
};
_containerView.transform = CGAffineTransformMakeScale(0.6f, 0.6f);
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{
_containerView.transform = CGAffineTransformIdentity;
} completion:nil];
[UIView animateWithDuration:0.3f animations:^
{
_containerView.alpha = 1.0f;
} completion:^(__unused BOOL finished) {
dismissBlock();
}];
dispatchAfter(0.15, dispatch_get_main_queue(), ^{
[_spinner setSucceed];
});
}
- (BOOL)canBecomeFirstResponder {
return false;
}
@end

View File

@ -0,0 +1,12 @@
#import <UIKit/UIKit.h>
@interface ProxyWindowController : UIViewController
- (instancetype)initWithLight:(bool)light text:(NSString *)text icon:(UIImage *)icon isShield:(bool)isShield;
- (void)dismissWithSuccess:(void (^)(void))completion increasedDelay:(bool)increasedDelay;
- (void)updateLayout;
+ (UIImage *)generateShieldImage:(bool)isLight;
@end

View File

@ -0,0 +1,573 @@
#import "ProxyWindow.h"
#define UIColorRGB(rgb) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:1.0f])
#define UIColorRGBA(rgb,a) ([[UIColor alloc] initWithRed:(((rgb >> 16) & 0xff) / 255.0f) green:(((rgb >> 8) & 0xff) / 255.0f) blue:(((rgb) & 0xff) / 255.0f) alpha:a])
#ifdef __LP64__
# define CGFloor floor
#else
# define CGFloor floorf
#endif
static inline void dispatchAfter(double delay, dispatch_queue_t queue, dispatch_block_t block)
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)((delay) * NSEC_PER_SEC)), queue, block);
}
static UIFont *mediumSystemFontOfSize(CGFloat size) {
static bool useSystem = false;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
useSystem = [[[UIDevice currentDevice] systemVersion] intValue] >= 9;
});
if (useSystem) {
return [UIFont systemFontOfSize:size weight:UIFontWeightMedium];
} else {
return [UIFont fontWithName:@"HelveticaNeue-Medium" size:size];
}
}
static bool readCGFloat(NSString *string, int *position, CGFloat *result) {
int start = *position;
bool seenDot = false;
int length = (int)string.length;
while (*position < length) {
unichar c = [string characterAtIndex:*position];
*position++;
if (c == '.') {
if (seenDot) {
return false;
} else {
seenDot = true;
}
} else if ((c < '0' || c > '9') && c != '-') {
if (*position == start) {
*result = 0.0f;
return true;
} else {
*result = [[string substringWithRange:NSMakeRange(start, *position - start)] floatValue];
return true;
}
}
}
if (*position == start) {
*result = 0.0f;
return true;
} else {
*result = [[string substringWithRange:NSMakeRange(start, *position - start)] floatValue];
return true;
}
return true;
}
static void drawSvgPath(CGContextRef context, NSString *path) {
int position = 0;
int length = (int)path.length;
while (position < length) {
unichar c = [path characterAtIndex:position];
position++;
if (c == ' ') {
continue;
}
if (c == 'M') { // M
CGFloat x = 0.0f;
CGFloat y = 0.0f;
readCGFloat(path, &position, &x);
readCGFloat(path, &position, &y);
CGContextMoveToPoint(context, x, y);
} else if (c == 'L') { // L
CGFloat x = 0.0f;
CGFloat y = 0.0f;
readCGFloat(path, &position, &x);
readCGFloat(path, &position, &y);
CGContextAddLineToPoint(context, x, y);
} else if (c == 'C') { // C
CGFloat x1 = 0.0f;
CGFloat y1 = 0.0f;
CGFloat x2 = 0.0f;
CGFloat y2 = 0.0f;
CGFloat x = 0.0f;
CGFloat y = 0.0f;
readCGFloat(path, &position, &x1);
readCGFloat(path, &position, &y1);
readCGFloat(path, &position, &x2);
readCGFloat(path, &position, &y2);
readCGFloat(path, &position, &x);
readCGFloat(path, &position, &y);
CGContextAddCurveToPoint(context, x1, y1, x2, y2, x, y);
} else if (c == 'Z') { // Z
CGContextClosePath(context);
CGContextFillPath(context);
CGContextBeginPath(context);
} else if (c == 'S') { // Z
CGContextClosePath(context);
CGContextStrokePath(context);
CGContextBeginPath(context);
} else if (c == 'U') { // Z
CGContextStrokePath(context);
CGContextBeginPath(context);
}
}
}
static bool ProxyWindowIsLight = true;
@interface ProxySpinnerView : UIView
@property (nonatomic, copy) void (^onSuccess)(void);
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
- (void)setSucceed;
@end
@interface ProxyWindowController ()
{
bool _light;
NSString *_text;
UIImage *_icon;
bool _isShield;
UIVisualEffectView *_effectView;
UIView *_backgroundView;
ProxySpinnerView *_spinner;
UIImageView *_shield;
UILabel *_label;
}
@property (nonatomic, weak) UIWindow *weakWindow;
@property (nonatomic, strong) UIView *containerView;
@end
@implementation ProxyWindowController
+ (UIImage *)generateShieldImage:(bool)isLight {
UIColor *color = isLight ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
NSString *code = @"M100,6.56393754 L6,48.2657557 L6,110.909091 C6,169.509174 46.3678836,223.966692 100,237.814087 C153.632116,223.966692 194,169.509174 194,110.909091 L194,48.2657557 L100,6.56393754 S";
UIGraphicsBeginImageContextWithOptions(CGSizeMake(67, 82), false, 0.0f);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextScaleCTM(context, 0.333333f, 0.333333f);
CGContextSetLineWidth(context, 12.0f);
CGContextSetStrokeColorWithColor(context, color.CGColor);
drawSvgPath(context, code);
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
- (instancetype)initWithLight:(bool)light text:(NSString *)text icon:(UIImage *)icon isShield:(bool)isShield {
self = [super init];
if (self != nil) {
_light = light;
_text = text;
_icon = icon;
_isShield = isShield;
}
return self;
}
- (void)loadView
{
[super loadView];
if (self.view.bounds.size.width > FLT_EPSILON) {
[self updateLayout];
}
}
- (void)updateLayout {
CGSize spinnerSize = CGSizeMake(48.0, 48.0);
CGSize containerSize = CGSizeMake(156.0, 176.0);
if (_icon == nil) {
containerSize = CGSizeMake(207.0, 177.0);
spinnerSize = CGSizeMake(40.0, 40.0);
}
if (_text.length != 0) {
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineBreakMode = NSLineBreakByWordWrapping;
style.lineSpacing = 2.0f;
style.alignment = NSTextAlignmentCenter;
NSDictionary *attributes = @{NSForegroundColorAttributeName:_light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor], NSFontAttributeName:mediumSystemFontOfSize(17.0f), NSParagraphStyleAttributeName:style};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:15.0f];
label.numberOfLines = 0;
label.textAlignment = NSTextAlignmentCenter;
label.attributedText = string;
CGSize labelSize = [label sizeThatFits:CGSizeMake(containerSize.width - 10.0 * 2.0, CGFLOAT_MAX)];
containerSize.height += labelSize.height - 38.0;
}
CGRect spinnerFrame = CGRectMake((containerSize.width - spinnerSize.width) / 2.0f, _icon != nil ? 40.0f : 45.0, spinnerSize.width, spinnerSize.height);
if (_containerView == nil) {
_containerView = [[UIView alloc] initWithFrame:CGRectMake(CGFloor(self.view.frame.size.width - containerSize.width) / 2, CGFloor(self.view.frame.size.height - containerSize.height) / 2, containerSize.width, containerSize.height)];
_containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
_containerView.alpha = 0.0f;
_containerView.clipsToBounds = true;
_containerView.layer.cornerRadius = 20.0f;
[self.view addSubview:_containerView];
if ([[[UIDevice currentDevice] systemVersion] intValue] >= 9) {
_effectView = [[UIVisualEffectView alloc] initWithEffect:_light ? [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight] : [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]];
_effectView.frame = _containerView.bounds;
[_containerView addSubview:_effectView];
if (_light)
{
UIView *tintView = [[UIView alloc] initWithFrame:_effectView.bounds];
tintView.backgroundColor = UIColorRGBA(0xf4f4f4, 0.75f);
[_containerView addSubview:tintView];
}
} else {
_backgroundView = [[UIView alloc] initWithFrame:_containerView.bounds];
_backgroundView.backgroundColor = _light ? UIColorRGBA(0xeaeaea, 0.92f) : UIColorRGBA(0x000000, 0.9f);
[_containerView addSubview:_backgroundView];
}
UIColor *color = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
UIImage *image = nil;
if (_icon != nil) {
image = _icon;
} else {
CGSize size = CGSizeMake(66.0, 66.0);
UIGraphicsBeginImageContextWithOptions(size, false, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, color.CGColor);
CGFloat lineWidth = 4.0f;
CGContextSetLineWidth(context, lineWidth);
CGContextStrokeEllipseInRect(context, CGRectMake(lineWidth / 2.0f, lineWidth / 2.0f, size.width - lineWidth, size.height - lineWidth));
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
_shield = [[UIImageView alloc] initWithImage:image];
_shield.frame = CGRectMake((_containerView.frame.size.width - _shield.frame.size.width) / 2.0f, _isShield ? 23.0f : 30.0, _shield.frame.size.width, _shield.frame.size.height);
[_containerView addSubview:_shield];
_spinner = [[ProxySpinnerView alloc] initWithFrame:spinnerFrame light:_light];
[_containerView addSubview:_spinner];
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.lineBreakMode = NSLineBreakByWordWrapping;
style.lineSpacing = 2.0f;
style.alignment = NSTextAlignmentCenter;
NSDictionary *attributes = @{NSForegroundColorAttributeName:_light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor], NSFontAttributeName:mediumSystemFontOfSize(17.0f), NSParagraphStyleAttributeName:style};
NSAttributedString *string = [[NSAttributedString alloc] initWithString:_text attributes:attributes];
UILabel *label = [[UILabel alloc] init];
label.font = [UIFont systemFontOfSize:15.0f];
label.numberOfLines = 0;
label.textAlignment = NSTextAlignmentCenter;
label.attributedText = string;
_label = label;
CGSize labelSize = [label sizeThatFits:CGSizeMake(_containerView.frame.size.width - 10.0 * 2.0, CGFLOAT_MAX)];
label.frame = CGRectMake((_containerView.frame.size.width - labelSize.width) / 2.0f, _containerView.frame.size.height - labelSize.height - 18.0f, labelSize.width, labelSize.height);
[_containerView addSubview:label];
} else {
_containerView.frame = CGRectMake(CGFloor(self.view.frame.size.width - containerSize.width) / 2, CGFloor(self.view.frame.size.height - containerSize.width) / 2, containerSize.width, containerSize.height);
_effectView.frame = _containerView.bounds;
_backgroundView.frame = _containerView.bounds;
_spinner.frame = spinnerFrame;
_shield.frame = CGRectMake((_containerView.frame.size.width - _shield.frame.size.width) / 2.0f, _isShield ? 23.0f : 30.0, _shield.frame.size.width, _shield.frame.size.height);
[_label sizeToFit];
_label.frame = CGRectMake((_containerView.frame.size.width - _label.frame.size.width) / 2.0f, _containerView.frame.size.height - _label.frame.size.height - 18.0f, _label.frame.size.width, _label.frame.size.height);
}
}
- (void)dismissWithSuccess:(void (^)(void))completion increasedDelay:(bool)increasedDelay
{
void (^dismissBlock)(void) = ^{
[UIView animateWithDuration:0.3 delay:increasedDelay ? 2.1 : 0.55 options:0 animations:^{
_containerView.alpha = 0.0f;
} completion:^(__unused BOOL finished) {
if (completion) {
completion();
}
}];
};
_containerView.transform = CGAffineTransformMakeScale(0.6f, 0.6f);
[UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{
_containerView.transform = CGAffineTransformIdentity;
} completion:nil];
[UIView animateWithDuration:0.3f animations:^{
_containerView.alpha = 1.0f;
} completion:^(__unused BOOL finished) {
dismissBlock();
}];
if (_icon == nil) {
dispatchAfter(0.15, dispatch_get_main_queue(), ^{
[_spinner setSucceed];
});
}
}
- (BOOL)canBecomeFirstResponder {
return false;
}
@end
@interface ProxySpinnerViewInternal : UIView
@property (nonatomic, copy) void (^onDraw)(void);
@property (nonatomic, copy) void (^onSuccess)(void);
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress;
@end
@interface ProxySpinnerView ()
{
ProxySpinnerViewInternal *_internalView;
bool _progressing;
}
@end
@implementation ProxySpinnerView
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
self = [super initWithFrame:frame];
if (self != nil) {
self.backgroundColor = [UIColor clearColor];
self.opaque = false;
self.userInteractionEnabled = false;
_internalView = [[ProxySpinnerViewInternal alloc] initWithFrame:self.bounds light:light];
_internalView.hidden = true;
[self addSubview:_internalView];
}
return self;
}
- (void)setSucceed {
_internalView.hidden = false;
[_internalView setSucceed:false progress:0.0f];
}
@end
@interface ProxySpinnerViewInternal ()
{
CADisplayLink *_displayLink;
bool _light;
bool _isProgressing;
CGFloat _rotationValue;
bool _isRotating;
CGFloat _checkValue;
bool _delay;
bool _isSucceed;
bool _isChecking;
NSTimeInterval _previousTime;
}
@end
@implementation ProxySpinnerViewInternal
- (instancetype)initWithFrame:(CGRect)frame light:(bool)light {
self = [super initWithFrame:frame];
if (self != nil) {
_light = light;
self.backgroundColor = [UIColor clearColor];
self.opaque = false;
self.userInteractionEnabled = false;
}
return self;
}
- (void)dealloc {
_displayLink.paused = true;
[_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (CADisplayLink *)displayLink {
if (_displayLink == nil) {
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkUpdate)];
_displayLink.paused = true;
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
return _displayLink;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f);
CGFloat lineWidth = 4.0f;
CGFloat inset = 3.0f;
if (rect.size.width < 44.0) {
inset = 0.0f;
}
UIColor *foregroundColor = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor];
CGContextSetFillColorWithColor(context, foregroundColor.CGColor);
CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor);
if (_isProgressing)
{
CGMutablePathRef path = CGPathCreateMutable();
CGFloat offset = -_rotationValue * 2.0f * M_PI;
CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2) * (1.0f - _checkValue), false);
CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10);
CGContextAddPath(context, strokedArc);
CGPathRelease(strokedArc);
CGPathRelease(path);
CGContextFillPath(context);
}
if (_checkValue > FLT_EPSILON)
{
CGContextSetLineWidth(context, 4.0f);
CGContextSetLineCap(context, kCGLineCapRound);
CGContextSetLineJoin(context, kCGLineJoinRound);
CGContextSetMiterLimit(context, 10);
CGFloat firstSegment = MIN(1.0f, _checkValue * 3.0f);
CGPoint s = CGPointMake(inset + 5.0f, centerPoint.y + 1.0f);
CGPoint p1 = CGPointMake(10.0f, 10.0f);
CGPoint p2 = CGPointMake(23.0f, -23.0f);
if (rect.size.width < 44.0) {
p1 = CGPointMake(9.0f, 9.0f);
p2 = CGPointMake(23.0f, -23.0f);
}
if (firstSegment < 1.0f)
{
CGContextMoveToPoint(context, s.x + p1.x * firstSegment, s.y + p1.y * firstSegment);
CGContextAddLineToPoint(context, s.x, s.y);
}
else
{
CGFloat secondSegment = (_checkValue - 0.33f) * 1.5f;
if (rect.size.width < 44.0) {
secondSegment = (_checkValue - 0.33f) * 1.35f;
}
CGContextMoveToPoint(context, s.x + p1.x + p2.x * secondSegment, s.y + p1.y + p2.y * secondSegment);
CGContextAddLineToPoint(context, s.x + p1.x, s.y + p1.y);
CGContextAddLineToPoint(context, s.x, s.y);
}
CGContextStrokePath(context);
}
}
- (void)displayLinkUpdate
{
NSTimeInterval previousTime = _previousTime;
NSTimeInterval currentTime = CACurrentMediaTime();
_previousTime = currentTime;
NSTimeInterval delta = previousTime > DBL_EPSILON ? currentTime - previousTime : 0.0;
if (delta < DBL_EPSILON)
return;
if (_isRotating)
{
_rotationValue += delta * 1.35f;
}
if (_isSucceed && _isRotating && !_delay && _rotationValue >= 0.5f)
{
_rotationValue = 0.5f;
_isRotating = false;
_isChecking = true;
}
if (_isChecking)
_checkValue += delta * M_PI * 1.6f;
if (_rotationValue > 1.0f)
{
_rotationValue = 0.0f;
_delay = false;
}
if (_checkValue > 1.0f)
{
_checkValue = 1.0f;
[self displayLink].paused = true;
if (self.onSuccess != nil)
{
void (^onSuccess)(void) = [self.onSuccess copy];
self.onSuccess = nil;
onSuccess();
}
}
[self setNeedsDisplay];
if (self.onDraw != nil)
{
void (^onDraw)(void) = [self.onDraw copy];
self.onDraw = nil;
onDraw();
}
}
- (void)setProgress {
_isRotating = true;
_isProgressing = true;
[self displayLink].paused = false;
}
- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress {
if (_isSucceed)
return;
if (fromRotation) {
_isRotating = true;
_isProgressing = true;
_rotationValue = progress;
}
_isSucceed = true;
if (!_isRotating)
_isChecking = true;
else if (_rotationValue > 0.5f)
_delay = true;
[self displayLink].paused = false;
}
- (bool)isSucceed
{
return _isSucceed;
}
@end

View File

@ -23,6 +23,7 @@ static_library(
"//submodules/PasswordSetupUI:PasswordSetupUI",
"//submodules/AppBundle:AppBundle",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -332,7 +332,7 @@ public final class SecureIdAuthController: ViewController {
return
}
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
strongSelf.navigationItem.rightBarButtonItem = item
strongSelf.deleteDisposable.set((deleteSecureIdValues(network: strongSelf.context.account.network, keys: Set(values.map({ $0.value.key })))
|> deliverOnMainQueue).start(completed: {
@ -387,7 +387,7 @@ public final class SecureIdAuthController: ViewController {
if previousHadProgress != updatedHasProgress {
if updatedHasProgress {
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: self.presentationData.theme))
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.controlColor))
self.navigationItem.rightBarButtonItem = item
} else {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationInfoIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.infoPressed))

View File

@ -6,6 +6,7 @@ import Postbox
import TelegramCore
import TelegramPresentationData
import TextFormat
import Markdown
private let infoFont = Font.regular(14.0)
private let passwordFont = Font.regular(16.0)

View File

@ -118,7 +118,7 @@ final class SecureIdDocumentFormController: FormController<SecureIdDocumentFormS
if let strongSelf = self {
switch state {
case .inProgress:
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
case .saveAvailable, .saveNotAvailable:
if strongSelf.navigationItem.rightBarButtonItem !== strongSelf.doneItem {
strongSelf.navigationItem.rightBarButtonItem = strongSelf.doneItem

View File

@ -71,7 +71,7 @@ public final class SecureIdPlaintextFormController: FormController<SecureIdPlain
if let strongSelf = self {
switch state {
case .inProgress:
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
case .nextAvailable, .nextNotAvailable:
if strongSelf.navigationItem.rightBarButtonItem !== strongSelf.nextItem {
strongSelf.navigationItem.rightBarButtonItem = strongSelf.nextItem

View File

@ -122,7 +122,7 @@ public class SetupTwoStepVerificationController: ViewController {
case .none:
item = nil
case .activity:
item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
case let .button(title, _):
item = UIBarButtonItem(title: title, style: .done, target: strongSelf, action: #selector(strongSelf.nextPressed))
}

View File

@ -57,6 +57,7 @@ static_library(
"//submodules/ContactListUI:ContactListUI",
"//submodules/ContextUI:ContextUI",
"//submodules/AppBundle:AppBundle",
"//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -8,6 +8,7 @@ import ItemListUI
import PresentationDataUtils
import TextFormat
import AppBundle
import Markdown
class ChannelDiscussionGroupSetupHeaderItem: ListViewItem, ItemListItem {
let theme: PresentationTheme

View File

@ -24,6 +24,7 @@ import PeerAvatarGalleryUI
import NotificationMuteSettingsUI
import MapResourceToAvatarSizes
import NotificationSoundSelectionUI
import Markdown
private final class ChannelInfoControllerArguments {
let account: Account

View File

@ -12,6 +12,7 @@ import AccountContext
import AlertUI
import PresentationDataUtils
import PasswordSetupUI
import Markdown
private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UITextFieldDelegate {
private var theme: PresentationTheme

View File

@ -50,7 +50,7 @@ final class ChannelStatsController: ViewController {
return
}
if value {
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: strongSelf.presentationData.theme))
strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: strongSelf.presentationData.theme.rootController.navigationBar.controlColor))
} else {
strongSelf.navigationItem.rightBarButtonItem = nil
}

View File

@ -36,6 +36,7 @@ import MapResourceToAvatarSizes
import NotificationSoundSelectionUI
import ItemListAddressItem
import AppBundle
import Markdown
private final class GroupInfoArguments {
let context: AccountContext

Some files were not shown because too many files have changed in this diff Show More