diff --git a/BUCK b/BUCK
index 5122ec3b5a..934c87404c 100644
--- a/BUCK
+++ b/BUCK
@@ -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",
diff --git a/Config/buck_rule_macros.bzl b/Config/buck_rule_macros.bzl
index 609946e1f9..57aad16c62 100644
--- a/Config/buck_rule_macros.bzl
+++ b/Config/buck_rule_macros.bzl
@@ -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,
diff --git a/Config/configs.bzl b/Config/configs.bzl
index b52954d5c3..75fa2d15c3 100644
--- a/Config/configs.bzl
+++ b/Config/configs.bzl
@@ -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 = {
diff --git a/Config/utils.bzl b/Config/utils.bzl
index bb2b6c70f4..ca02712754 100644
--- a/Config/utils.bzl
+++ b/Config/utils.bzl
@@ -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")
diff --git a/Config/wallet_configs.bzl b/Config/wallet_configs.bzl
new file mode 100644
index 0000000000..50b9fe9d17
--- /dev/null
+++ b/Config/wallet_configs.bzl
@@ -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
diff --git a/Makefile b/Makefile
index 1d46ca4e7a..0f4fcc01df 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/Wallet/BUCK b/Wallet/BUCK
index 0e95e9f847..907d1ded34 100644
--- a/Wallet/BUCK
+++ b/Wallet/BUCK
@@ -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"),
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x-1.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x-1.png
new file mode 100644
index 0000000000..dd360d8f50
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x-1.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x.png
new file mode 100644
index 0000000000..2e502e7dab
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@2x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@3x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@3x.png
new file mode 100644
index 0000000000..c47aeed4b1
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIcon@3x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad.png
new file mode 100644
index 0000000000..5eaf0bab23
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad@2x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad@2x.png
new file mode 100644
index 0000000000..6d9e7ab98c
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconIpad@2x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconLargeIpad@2x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconLargeIpad@2x.png
new file mode 100644
index 0000000000..9bf363744d
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueIconLargeIpad@2x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon.png
new file mode 100644
index 0000000000..dc5916282e
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x-1.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x-1.png
new file mode 100644
index 0000000000..0898af42d9
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x-1.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x.png
new file mode 100644
index 0000000000..0898af42d9
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@2x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@3x.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@3x.png
new file mode 100644
index 0000000000..f7725e9914
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/BlueNotificationIcon@3x.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Contents.json b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Contents.json
new file mode 100644
index 0000000000..00dd28927f
--- /dev/null
+++ b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Contents.json
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple-iTunesArtwork.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple-iTunesArtwork.png
new file mode 100644
index 0000000000..f00a2857f0
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple-iTunesArtwork.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@29x29.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@29x29.png
new file mode 100644
index 0000000000..90d7b67bc0
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@29x29.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@40x40-1.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@40x40-1.png
new file mode 100644
index 0000000000..a79cb5dcdc
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@40x40-1.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58-1.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58-1.png
new file mode 100644
index 0000000000..aa6a4a442e
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58-1.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58.png
new file mode 100644
index 0000000000..aa6a4a442e
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@58x58.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80-1.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80-1.png
new file mode 100644
index 0000000000..385bc474b2
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80-1.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80.png
new file mode 100644
index 0000000000..385bc474b2
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@80x80.png differ
diff --git a/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@87x87.png b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@87x87.png
new file mode 100644
index 0000000000..c0a9ce9319
Binary files /dev/null and b/Wallet/Icons.xcassets/AppIconLLC.appiconset/Simple@87x87.png differ
diff --git a/Wallet/Icons.xcassets/Contents.json b/Wallet/Icons.xcassets/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/Wallet/Icons.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/Wallet/Info.plist b/Wallet/Info.plist
index d8ada8d0f8..1dcbb9f34b 100644
--- a/Wallet/Info.plist
+++ b/Wallet/Info.plist
@@ -12,166 +12,20 @@
$(EXECUTABLE_NAME)
CFBundleIcons
- CFBundleAlternateIcons
-
- Black
-
- CFBundleIconFiles
-
- BlackIcon
- BlackNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlackClassic
-
- CFBundleIconFiles
-
- BlackClassicIcon
- BlackClassicNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlackFilled
-
- CFBundleIconFiles
-
- BlackFilledIcon
-
- UIPrerenderedIcon
-
-
- Blue
-
- CFBundleIconFiles
-
- BlueIcon
- BlueNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlueClassic
-
- CFBundleIconFiles
-
- BlueClassicIcon
- BlueClassicNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlueFilled
-
- CFBundleIconFiles
-
- BlueFilledIcon
-
- UIPrerenderedIcon
-
-
- WhiteFilled
-
- CFBundleIconFiles
-
- WhiteFilledIcon
-
- UIPrerenderedIcon
-
-
-
CFBundlePrimaryIcon
CFBundleIconName
- AppIconLLC
+ AppIconWallet
UIPrerenderedIcon
CFBundleIcons~ipad
- CFBundleAlternateIcons
-
- Black
-
- CFBundleIconFiles
-
- BlackIconIpad
- BlackIconLargeIpad
- BlackNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlackClassic
-
- CFBundleIconFiles
-
- BlackClassicIconIpad
- BlackClassicIconLargeIpad
- BlackClassicNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlackFilled
-
- CFBundleIconFiles
-
- BlackFilledIconIpad
- BlackFilledIconLargeIpad
-
- UIPrerenderedIcon
-
-
- Blue
-
- CFBundleIconFiles
-
- BlueIconIpad
- BlueIconLargeIpad
- BlueNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlueClassic
-
- CFBundleIconFiles
-
- BlueClassicIconIpad
- BlueClassicIconLargeIpad
- BlueClassicNotificationIcon
-
- UIPrerenderedIcon
-
-
- BlueFilled
-
- CFBundleIconFiles
-
- BlueFilledIconIpad
- BlueFilledIconLargeIpad
-
- UIPrerenderedIcon
-
-
- WhiteFilled
-
- CFBundleIconFiles
-
- WhiteFilledIcon
-
- UIPrerenderedIcon
-
-
-
CFBundlePrimaryIcon
CFBundleIconName
- AppIconLLC
+ AppIconWallet
UIPrerenderedIcon
@@ -190,27 +44,6 @@
????
CFBundleURLTypes
-
- CFBundleTypeRole
- Viewer
- CFBundleURLName
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleURLSchemes
-
- telegram
-
-
-
- CFBundleTypeRole
- Viewer
- CFBundleURLName
- $(PRODUCT_BUNDLE_IDENTIFIER).compatibility
- CFBundleURLSchemes
-
- tg
- $(APP_SPECIFIC_URL_SCHEME)
-
-
CFBundleTypeRole
Viewer
@@ -226,38 +59,6 @@
${BUILD_NUMBER}
ITSAppUsesNonExemptEncryption
- LSApplicationQueriesSchemes
-
- instagram
- comgooglemaps-x-callback
- foursquare
- here-location
- yandexmaps
- yandexnavi
- comgooglemaps
- youtube
- twitter
- vk
- waze
- googlechrome
- googlechromes
- firefox
- touch-http
- touch-https
- yandexbrowser-open-url
- vimeo
- vine
- coub
- uber
- citymapper
- lyft
- opera-http
- opera-https
- firefox-focus
- ddgQuickLink
- moovit
- alook
-
LSRequiresIPhoneOS
NSAppTransportSecurity
@@ -265,43 +66,8 @@
NSAllowsArbitraryLoads
- NSCameraUsageDescription
- We need this so that you can take and share photos and videos.
- NSContactsUsageDescription
- Telegram stores your contacts heavily encrypted in the cloud to let you connect with your friends across all your devices.
NSFaceIDUsageDescription
- You can use Face ID to unlock the app.
- NSLocationAlwaysUsageDescription
- 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.
- NSLocationWhenInUseUsageDescription
- When you send your location to your friends, Telegram needs access to show them a map.
- NSMicrophoneUsageDescription
- We need this so that you can record and share voice messages and videos with sound.
- NSMotionUsageDescription
- When you send your location to your friends, Telegram needs access to show them a map.
- NSPhotoLibraryAddUsageDescription
- We need this so that you can share photos and videos from your photo library.
- NSPhotoLibraryUsageDescription
- We need this so that you can share photos and videos from your photo library.
- NSSiriUsageDescription
- You can use Siri to send messages.
- NSUserActivityTypes
-
- INSendMessageIntent
- RemindAboutChatIntent
-
- UIAppFonts
-
- SFCompactRounded-Semibold.otf
-
- UIBackgroundModes
-
- audio
- fetch
- location
- remote-notification
- voip
-
+ For better security, please allow TON Wallet to use your Face ID to authenticate payments.
UIDeviceFamily
1
@@ -338,29 +104,5 @@
UIViewGroupOpacity
- UTImportedTypeDeclarations
-
-
- UTTypeConformsTo
-
- public.data
-
- UTTypeDescription
- Telegram iOS Color Theme File
- UTTypeIconFiles
-
- BlueIcon@3x.png
-
- UTTypeIdentifier
- org.telegram.Telegram-iOS.theme
- UTTypeTagSpecification
-
- public.filename-extension
-
- tgios-theme
-
-
-
-
diff --git a/Wallet/InfoPlistStrings/en.lproj/InfoPlist.strings b/Wallet/InfoPlistStrings/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..1fc9a923ce
--- /dev/null
+++ b/Wallet/InfoPlistStrings/en.lproj/InfoPlist.strings
@@ -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.";
diff --git a/Wallet/LaunchScreen.xib b/Wallet/LaunchScreen.xib
new file mode 100644
index 0000000000..9584dae552
--- /dev/null
+++ b/Wallet/LaunchScreen.xib
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Wallet/Sources/AppDelegate.swift b/Wallet/Sources/AppDelegate.swift
index 9fb7caa44a..bcffbee986 100644
--- a/Wallet/Sources/AppDelegate.swift
+++ b/Wallet/Sources/AppDelegate.swift
@@ -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 {
+ return .single(true)
+ }
+
+ func getServerSalt() -> Signal {
+ 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()
diff --git a/Wallet/Strings/ar.lproj/Localizable.strings b/Wallet/Strings/ar.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/ca.lproj/Localizable.strings b/Wallet/Strings/ca.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/de.lproj/Localizable.strings b/Wallet/Strings/de.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/en.lproj/Localizable.strings b/Wallet/Strings/en.lproj/Localizable.strings
new file mode 100644
index 0000000000..9697a8a2ba
--- /dev/null
+++ b/Wallet/Strings/en.lproj/Localizable.strings
@@ -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" = "You’re 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" = "Let’s 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";
diff --git a/Wallet/Strings/es.lproj/Localizable.strings b/Wallet/Strings/es.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/fr.lproj/Localizable.strings b/Wallet/Strings/fr.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/id.lproj/Localizable.strings b/Wallet/Strings/id.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/it.lproj/Localizable.strings b/Wallet/Strings/it.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/ko.lproj/Localizable.strings b/Wallet/Strings/ko.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/ms.lproj/Localizable.strings b/Wallet/Strings/ms.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/nl.lproj/Localizable.strings b/Wallet/Strings/nl.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/pt.lproj/Localizable.strings b/Wallet/Strings/pt.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/ru.lproj/Localizable.strings b/Wallet/Strings/ru.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/tr.lproj/Localizable.strings b/Wallet/Strings/tr.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Strings/uk.lproj/Localizable.strings b/Wallet/Strings/uk.lproj/Localizable.strings
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/Wallet/Wallet.entitlements b/Wallet/Wallet.entitlements
new file mode 100644
index 0000000000..6631ffa6f2
--- /dev/null
+++ b/Wallet/Wallet.entitlements
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/submodules/AccountContext/BUCK b/submodules/AccountContext/BUCK
index 255438a28a..896ec1bb9d 100644
--- a/submodules/AccountContext/BUCK
+++ b/submodules/AccountContext/BUCK
@@ -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",
diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift
index 03a75f467e..0540efdaa0 100644
--- a/submodules/AccountContext/Sources/AccountContext.swift
+++ b/submodules/AccountContext/Sources/AccountContext.swift
@@ -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 { get }
var currentLimitsConfiguration: Atomic { get }
diff --git a/submodules/ActivityIndicator/BUCK b/submodules/ActivityIndicator/BUCK
index aa2ce7825c..fd29559549 100644
--- a/submodules/ActivityIndicator/BUCK
+++ b/submodules/ActivityIndicator/BUCK
@@ -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",
diff --git a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift
index ef3162a27f..205ef1c454 100644
--- a/submodules/ActivityIndicator/Sources/ActivityIndicator.swift
+++ b/submodules/ActivityIndicator/Sources/ActivityIndicator.swift
@@ -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 {
diff --git a/submodules/AnimatedStickerNode/BUCK b/submodules/AnimatedStickerNode/BUCK
new file mode 100644
index 0000000000..bdc95d5051
--- /dev/null
+++ b/submodules/AnimatedStickerNode/BUCK
@@ -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",
+ ],
+)
diff --git a/submodules/AnimationUI/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
similarity index 89%
rename from submodules/AnimationUI/Sources/AnimatedStickerNode.swift
rename to submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
index d6c0259194..92df089561 100644
--- a/submodules/AnimationUI/Sources/AnimatedStickerNode.swift
+++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift
@@ -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
+ func directDataPath() -> Signal
+}
+
+public final class AnimatedStickerNodeLocalFileSource: AnimatedStickerNodeSource {
+ public let path: String
+
+ public init(path: String) {
+ self.path = path
+ }
+
+ public func directDataPath() -> Signal {
+ return .single(self.path)
+ }
+
+ public func cachedDataPath(width: Int, height: Int) -> Signal {
+ 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(value: nil)
- private var directData: Tuple4?
+ 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)
diff --git a/submodules/AnimationUI/Sources/AnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift
similarity index 100%
rename from submodules/AnimationUI/Sources/AnimationRenderer.swift
rename to submodules/AnimatedStickerNode/Sources/AnimationRenderer.swift
diff --git a/submodules/AnimationUI/Sources/SoftwareAnimationRenderer.swift b/submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift
similarity index 100%
rename from submodules/AnimationUI/Sources/SoftwareAnimationRenderer.swift
rename to submodules/AnimatedStickerNode/Sources/SoftwareAnimationRenderer.swift
diff --git a/submodules/AnimationUI/Sources/AnimationUI.h b/submodules/AnimationUI/Sources/AnimationUI.h
deleted file mode 100644
index bcb24521bd..0000000000
--- a/submodules/AnimationUI/Sources/AnimationUI.h
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// AnimationUI.h
-// AnimationUI
-//
-// Created by Peter on 8/1/19.
-// Copyright © 2019 Telegram Messenger LLP. All rights reserved.
-//
-
-#import
-
-//! 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
-
-
diff --git a/submodules/AuthorizationUI/BUCK b/submodules/AuthorizationUI/BUCK
index b0cf635904..435baaaa24 100644
--- a/submodules/AuthorizationUI/BUCK
+++ b/submodules/AuthorizationUI/BUCK
@@ -10,6 +10,7 @@ static_library(
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/Display:Display#shared",
"//submodules/TextFormat:TextFormat",
+ "//submodules/Markdown:Markdown",
"//submodules/TelegramPresentationData:TelegramPresentationData",
],
frameworks = [
diff --git a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift
index aea3869ed0..9cc005a6f5 100644
--- a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift
+++ b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift
@@ -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 {
diff --git a/submodules/BotPaymentsUI/BUCK b/submodules/BotPaymentsUI/BUCK
index 6b633815f2..caf603a34b 100644
--- a/submodules/BotPaymentsUI/BUCK
+++ b/submodules/BotPaymentsUI/BUCK
@@ -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",
diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift
index b959d65554..ac4c8706e4 100644
--- a/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift
+++ b/submodules/BotPaymentsUI/Sources/BotCheckoutInfoController.swift
@@ -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:
diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift
index dcefe1f5d2..e49e48a409 100644
--- a/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift
+++ b/submodules/BotPaymentsUI/Sources/BotCheckoutNativeCardEntryController.swift
@@ -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:
diff --git a/submodules/ContactsPeerItem/BUCK b/submodules/ContactsPeerItem/BUCK
index 52f3dd428a..145b5971c7 100644
--- a/submodules/ContactsPeerItem/BUCK
+++ b/submodules/ContactsPeerItem/BUCK
@@ -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",
diff --git a/submodules/ItemListAddressItem/BUCK b/submodules/ItemListAddressItem/BUCK
index f999b68ec5..ec8a651a03 100644
--- a/submodules/ItemListAddressItem/BUCK
+++ b/submodules/ItemListAddressItem/BUCK
@@ -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",
diff --git a/submodules/ItemListAvatarAndNameInfoItem/BUCK b/submodules/ItemListAvatarAndNameInfoItem/BUCK
index 76b3319fce..314681d12c 100644
--- a/submodules/ItemListAvatarAndNameInfoItem/BUCK
+++ b/submodules/ItemListAvatarAndNameInfoItem/BUCK
@@ -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",
diff --git a/submodules/ItemListPeerActionItem/BUCK b/submodules/ItemListPeerActionItem/BUCK
index ad1ce4cefe..604dc5c10a 100644
--- a/submodules/ItemListPeerActionItem/BUCK
+++ b/submodules/ItemListPeerActionItem/BUCK
@@ -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",
diff --git a/submodules/ItemListPeerItem/BUCK b/submodules/ItemListPeerItem/BUCK
index d634f0c766..465b5ecae0 100644
--- a/submodules/ItemListPeerItem/BUCK
+++ b/submodules/ItemListPeerItem/BUCK
@@ -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",
diff --git a/submodules/ItemListStickerPackItem/BUCK b/submodules/ItemListStickerPackItem/BUCK
index 3da5704d8b..c2b14e9cef 100644
--- a/submodules/ItemListStickerPackItem/BUCK
+++ b/submodules/ItemListStickerPackItem/BUCK
@@ -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",
diff --git a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
index 2c8664528f..f726e85c0c 100644
--- a/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
+++ b/submodules/ItemListStickerPackItem/Sources/ItemListStickerPackItem.swift
@@ -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
diff --git a/submodules/ItemListUI/BUCK b/submodules/ItemListUI/BUCK
index 47ab22d750..51163a8812 100644
--- a/submodules/ItemListUI/BUCK
+++ b/submodules/ItemListUI/BUCK
@@ -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",
diff --git a/submodules/ItemListUI/Sources/ItemListController.swift b/submodules/ItemListUI/Sources/ItemListController.swift
index c0ceb9b119..e59a178ae5 100644
--- a/submodules/ItemListUI/Sources/ItemListController.swift
+++ b/submodules/ItemListUI/Sources/ItemListController.swift
@@ -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)
diff --git a/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift b/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift
index fa17f13671..0cc2c753b4 100644
--- a/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift
+++ b/submodules/ItemListUI/Sources/Items/ItemListInfoItem.swift
@@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TextFormat
+import Markdown
public enum InfoListItemText {
case plain(String)
diff --git a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift
index 5840ca926c..6f67e65615 100644
--- a/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift
+++ b/submodules/ItemListUI/Sources/Items/ItemListTextItem.swift
@@ -5,6 +5,7 @@ import AsyncDisplayKit
import SwiftSignalKit
import TelegramPresentationData
import TextFormat
+import Markdown
public enum ItemListTextItemText {
case plain(String)
diff --git a/submodules/LanguageLinkPreviewUI/BUCK b/submodules/LanguageLinkPreviewUI/BUCK
index 705b6bac09..ccc998474e 100644
--- a/submodules/LanguageLinkPreviewUI/BUCK
+++ b/submodules/LanguageLinkPreviewUI/BUCK
@@ -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",
diff --git a/submodules/LanguageLinkPreviewUI/Sources/LanguageLinkPreviewContentNode.swift b/submodules/LanguageLinkPreviewUI/Sources/LanguageLinkPreviewContentNode.swift
index d7dd9e0a88..ca3377b5a7 100644
--- a/submodules/LanguageLinkPreviewUI/Sources/LanguageLinkPreviewContentNode.swift
+++ b/submodules/LanguageLinkPreviewUI/Sources/LanguageLinkPreviewContentNode.swift
@@ -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)?
diff --git a/submodules/Markdown/BUCK b/submodules/Markdown/BUCK
new file mode 100644
index 0000000000..a7e2e42fc3
--- /dev/null
+++ b/submodules/Markdown/BUCK
@@ -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",
+ ],
+)
diff --git a/submodules/TextFormat/Sources/Markdown.swift b/submodules/Markdown/Sources/Markdown.swift
similarity index 99%
rename from submodules/TextFormat/Sources/Markdown.swift
rename to submodules/Markdown/Sources/Markdown.swift
index 1857305339..fbb4108e0d 100644
--- a/submodules/TextFormat/Sources/Markdown.swift
+++ b/submodules/Markdown/Sources/Markdown.swift
@@ -1,6 +1,5 @@
import Foundation
import UIKit
-import Display
private let controlStartCharactersSet = CharacterSet(charactersIn: "[*")
private let controlCharactersSet = CharacterSet(charactersIn: "[]()*_-\\")
diff --git a/submodules/NotificationSoundSelectionUI/BUCK b/submodules/NotificationSoundSelectionUI/BUCK
index 8f391af75a..89ed604125 100644
--- a/submodules/NotificationSoundSelectionUI/BUCK
+++ b/submodules/NotificationSoundSelectionUI/BUCK
@@ -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",
diff --git a/submodules/OverlayStatusController/BUCK b/submodules/OverlayStatusController/BUCK
index d177a806b0..a148ac0dd5 100644
--- a/submodules/OverlayStatusController/BUCK
+++ b/submodules/OverlayStatusController/BUCK
@@ -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",
diff --git a/submodules/OverlayStatusController/Resources/Star@2x.png b/submodules/OverlayStatusController/Resources/Star@2x.png
new file mode 100644
index 0000000000..0cbe785016
Binary files /dev/null and b/submodules/OverlayStatusController/Resources/Star@2x.png differ
diff --git a/submodules/OverlayStatusController/Resources/Star@3x.png b/submodules/OverlayStatusController/Resources/Star@3x.png
new file mode 100644
index 0000000000..5da86261c6
Binary files /dev/null and b/submodules/OverlayStatusController/Resources/Star@3x.png differ
diff --git a/submodules/OverlayStatusController/Sources/OverlayStatusController.h b/submodules/OverlayStatusController/Sources/OverlayStatusController.h
deleted file mode 100644
index 6662916b18..0000000000
--- a/submodules/OverlayStatusController/Sources/OverlayStatusController.h
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// OverlayStatusController.h
-// OverlayStatusController
-//
-// Created by Peter on 8/4/19.
-// Copyright © 2019 Telegram Messenger LLP. All rights reserved.
-//
-
-#import
-
-//! 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
-
-
diff --git a/submodules/OverlayStatusController/Sources/OverlayStatusController.swift b/submodules/OverlayStatusController/Sources/OverlayStatusController.swift
index 0a730a7bcd..82fd76fe5b 100644
--- a/submodules/OverlayStatusController/Sources/OverlayStatusController.swift
+++ b/submodules/OverlayStatusController/Sources/OverlayStatusController.swift
@@ -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()
diff --git a/submodules/OverlayStatusController/Sources/ProgressSpinnerView.h b/submodules/OverlayStatusController/Sources/ProgressSpinnerView.h
new file mode 100644
index 0000000000..e1682c02a2
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProgressSpinnerView.h
@@ -0,0 +1,12 @@
+#import
+
+@interface ProgressSpinnerView : UIView
+
+@property (nonatomic, copy) void (^onSuccess)(void);
+
+- (instancetype)initWithFrame:(CGRect)frame light:(bool)light;
+
+- (void)setProgress;
+- (void)setSucceed;
+
+@end
diff --git a/submodules/OverlayStatusController/Sources/ProgressSpinnerView.m b/submodules/OverlayStatusController/Sources/ProgressSpinnerView.m
new file mode 100644
index 0000000000..402f24f9e6
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProgressSpinnerView.m
@@ -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
diff --git a/submodules/OverlayStatusController/Sources/ProgressWindow.h b/submodules/OverlayStatusController/Sources/ProgressWindow.h
new file mode 100644
index 0000000000..74bcc35412
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProgressWindow.h
@@ -0,0 +1,16 @@
+#import
+
+@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
diff --git a/submodules/OverlayStatusController/Sources/ProgressWindow.m b/submodules/OverlayStatusController/Sources/ProgressWindow.m
new file mode 100644
index 0000000000..7dda6129be
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProgressWindow.m
@@ -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
diff --git a/submodules/OverlayStatusController/Sources/ProxyWindow.h b/submodules/OverlayStatusController/Sources/ProxyWindow.h
new file mode 100644
index 0000000000..2576321ac1
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProxyWindow.h
@@ -0,0 +1,12 @@
+#import
+
+@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
diff --git a/submodules/OverlayStatusController/Sources/ProxyWindow.m b/submodules/OverlayStatusController/Sources/ProxyWindow.m
new file mode 100644
index 0000000000..c68ddd8bc5
--- /dev/null
+++ b/submodules/OverlayStatusController/Sources/ProxyWindow.m
@@ -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
+
+
diff --git a/submodules/PassportUI/BUCK b/submodules/PassportUI/BUCK
index 6910b1af26..fddf791f3f 100644
--- a/submodules/PassportUI/BUCK
+++ b/submodules/PassportUI/BUCK
@@ -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",
diff --git a/submodules/PassportUI/Sources/SecureIdAuthController.swift b/submodules/PassportUI/Sources/SecureIdAuthController.swift
index bc68678938..2e1a240525 100644
--- a/submodules/PassportUI/Sources/SecureIdAuthController.swift
+++ b/submodules/PassportUI/Sources/SecureIdAuthController.swift
@@ -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))
diff --git a/submodules/PassportUI/Sources/SecureIdAuthFormContentNode.swift b/submodules/PassportUI/Sources/SecureIdAuthFormContentNode.swift
index 2555e374f7..2fcb17ecae 100644
--- a/submodules/PassportUI/Sources/SecureIdAuthFormContentNode.swift
+++ b/submodules/PassportUI/Sources/SecureIdAuthFormContentNode.swift
@@ -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)
diff --git a/submodules/PassportUI/Sources/SecureIdDocumentFormController.swift b/submodules/PassportUI/Sources/SecureIdDocumentFormController.swift
index 5a18bf37a5..39f6de0509 100644
--- a/submodules/PassportUI/Sources/SecureIdDocumentFormController.swift
+++ b/submodules/PassportUI/Sources/SecureIdDocumentFormController.swift
@@ -118,7 +118,7 @@ final class SecureIdDocumentFormController: FormController UIImage? {
return generateImage(CGSize(width: diameter + shadowBlur * 2.0, height: diameter + shadowBlur * 2.0), rotatedContext: { size, context in
@@ -135,7 +136,7 @@ final class ReactionNode: ASDisplayNode {
renderSize = CGSize(width: intrinsicSize.width * 2.5, height: intrinsicSize.height * 2.5)
}
}
- self.animationNode.setup(resource: .localFile(path), width: Int(renderSize.width), height: Int(renderSize.height), mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: Int(renderSize.width), height: Int(renderSize.height), mode: .direct)
case .reply:
self.intrinsicOffset = CGPoint(x: 0.0, y: 0.0)
self.imageNode.image = UIImage(named: "Chat/Context Menu/ReactionReply", in: getAppBundle(), compatibleWith: nil)
diff --git a/submodules/SettingsUI/BUCK b/submodules/SettingsUI/BUCK
index fe9149899e..1a7bffaa8a 100644
--- a/submodules/SettingsUI/BUCK
+++ b/submodules/SettingsUI/BUCK
@@ -78,6 +78,7 @@ static_library(
"//submodules/AppBundle:AppBundle",
"//submodules/ContextUI:ContextUI",
"//submodules/WalletUI:WalletUI",
+ "//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift
index 193936bd8b..11f0cfc946 100644
--- a/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift
+++ b/submodules/SettingsUI/Sources/ChangePhoneNumberIntroController.swift
@@ -9,6 +9,7 @@ import AccountContext
import AlertUI
import PresentationDataUtils
import AppBundle
+import Markdown
private final class ChangePhoneNumberIntroControllerNode: ASDisplayNode {
var presentationData: PresentationData
diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift
index c8d7bbf311..63b633f971 100644
--- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift
+++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift
@@ -218,10 +218,7 @@ func logoutOptionsController(context: AccountContext, navigationController: Navi
presentControllerImpl?(alertController, nil)
})
- let hasWallets = availableWallets(postbox: context.account.postbox)
- |> map { wallets in
- return !wallets.wallets.isEmpty
- }
+ let hasWallets = context.hasWallets
let signal = combineLatest(queue: .mainQueue(),
context.sharedContext.presentationData,
diff --git a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationResetController.swift b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationResetController.swift
index 7e0ec4aaa6..8d88e326b6 100644
--- a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationResetController.swift
+++ b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationResetController.swift
@@ -11,6 +11,7 @@ import TextFormat
import AccountContext
import AlertUI
import PresentationDataUtils
+import Markdown
private final class TwoStepVerificationResetControllerArguments {
let updateEntryText: (String) -> Void
diff --git a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift
index 0c4ff4afc1..dee7a2a88c 100644
--- a/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift
+++ b/submodules/SettingsUI/Sources/Privacy and Security/TwoStepVerificationUnlockController.swift
@@ -13,6 +13,7 @@ import AccountContext
import AlertUI
import PresentationDataUtils
import PasswordSetupUI
+import Markdown
private final class TwoStepVerificationUnlockSettingsControllerArguments {
let updatePasswordText: (String) -> Void
diff --git a/submodules/SettingsUI/Sources/SettingsController.swift b/submodules/SettingsUI/Sources/SettingsController.swift
index 94cb9cc264..e117e8d3ba 100644
--- a/submodules/SettingsUI/Sources/SettingsController.swift
+++ b/submodules/SettingsUI/Sources/SettingsController.swift
@@ -1172,9 +1172,7 @@ public func settingsController(context: AccountContext, accountManager: AccountM
return context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
|> map { view -> Bool in
if #available(iOSApplicationExtension 10.3, iOS 10.3, *) {
- let appConfiguration = view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue
- let configuration = WalletConfiguration.with(appConfiguration: appConfiguration)
- return configuration.config != nil
+ return true
} else {
return false
}
diff --git a/submodules/SolidRoundedButtonNode/BUCK b/submodules/SolidRoundedButtonNode/BUCK
index 4dc8eed041..3ef457d48d 100644
--- a/submodules/SolidRoundedButtonNode/BUCK
+++ b/submodules/SolidRoundedButtonNode/BUCK
@@ -8,7 +8,6 @@ static_library(
deps = [
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
- "//submodules/TelegramPresentationData:TelegramPresentationData",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
index 815f2a20ac..b2d3675881 100644
--- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
+++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift
@@ -2,7 +2,6 @@ import Foundation
import UIKit
import AsyncDisplayKit
import Display
-import TelegramPresentationData
private let textFont: UIFont = Font.regular(16.0)
@@ -16,12 +15,6 @@ public final class SolidRoundedButtonTheme {
}
}
-public extension SolidRoundedButtonTheme {
- convenience init(theme: PresentationTheme) {
- self.init(backgroundColor: theme.list.itemCheckColors.fillColor, foregroundColor: theme.list.itemCheckColors.foregroundColor)
- }
-}
-
public final class SolidRoundedButtonNode: ASDisplayNode {
private var theme: SolidRoundedButtonTheme
diff --git a/submodules/StickerPackPreviewUI/BUCK b/submodules/StickerPackPreviewUI/BUCK
index 5aa327252d..6058deb7bd 100644
--- a/submodules/StickerPackPreviewUI/BUCK
+++ b/submodules/StickerPackPreviewUI/BUCK
@@ -21,7 +21,8 @@ static_library(
"//submodules/TextFormat:TextFormat",
"//submodules/MergeLists:MergeLists",
"//submodules/ActivityIndicator:ActivityIndicator",
- "//submodules/AnimationUI:AnimationUI",
+ "//submodules/AnimatedStickerNode:AnimatedStickerNode",
+ "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift
index 18c14c4ecf..942ef827bd 100644
--- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift
+++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift
@@ -7,7 +7,8 @@ import AsyncDisplayKit
import Postbox
import StickerResources
import AccountContext
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
final class StickerPackPreviewInteraction {
var previewedItem: StickerPreviewPeekItem?
@@ -108,7 +109,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
}
}
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- self.animationNode?.setup(resource: .resource(account, stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
self.animationNode?.visibility = self.isVisibleInGrid && self.interaction?.playAnimatedStickers ?? true
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
} else {
diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
index a075e54299..c56daa5f91 100644
--- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
+++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift
@@ -6,7 +6,8 @@ import Postbox
import TelegramCore
import SwiftSignalKit
import StickerResources
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
public enum StickerPreviewPeekItem: Equatable {
case pack(StickerPackItem)
@@ -91,7 +92,7 @@ private final class StickerPreviewPeekContentNode: ASDisplayNode, PeekController
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 400.0, height: 400.0))
- self.animationNode?.setup(resource: .resource(account, item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
+ self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
self.animationNode?.visibility = true
self.animationNode?.addSubnode(self.textNode)
} else {
diff --git a/submodules/TelegramAnimatedStickerNode/BUCK b/submodules/TelegramAnimatedStickerNode/BUCK
new file mode 100644
index 0000000000..e9b1668421
--- /dev/null
+++ b/submodules/TelegramAnimatedStickerNode/BUCK
@@ -0,0 +1,24 @@
+load("//Config:buck_rule_macros.bzl", "static_library")
+
+static_library(
+ name = "TelegramAnimatedStickerNode",
+ srcs = glob([
+ "Sources/**/*.swift",
+ ]),
+ deps = [
+ "//submodules/Postbox:Postbox#shared",
+ "//submodules/TelegramCore:TelegramCore#shared",
+ "//submodules/StickerResources:StickerResources",
+ "//submodules/MediaResources:MediaResources",
+ "//submodules/Tuples:Tuples",
+ "//submodules/AnimatedStickerNode:AnimatedStickerNode",
+ "//submodules/rlottie:RLottieBinding",
+ "//submodules/YuvConversion:YuvConversion",
+ "//submodules/GZip:GZip",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ "$SDKROOT/System/Library/Frameworks/UIKit.framework",
+ "$SDKROOT/System/Library/Frameworks/MobileCoreServices.framework",
+ ],
+)
diff --git a/submodules/AnimationUI/Sources/AnimatedStickerUtils.swift b/submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift
similarity index 100%
rename from submodules/AnimationUI/Sources/AnimatedStickerUtils.swift
rename to submodules/TelegramAnimatedStickerNode/Sources/AnimatedStickerUtils.swift
diff --git a/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift b/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift
new file mode 100644
index 0000000000..291ead72e2
--- /dev/null
+++ b/submodules/TelegramAnimatedStickerNode/Sources/TelegramAnimatedStickerNode.swift
@@ -0,0 +1,39 @@
+import Foundation
+import AnimatedStickerNode
+import SwiftSignalKit
+import Postbox
+import TelegramCore
+import MediaResources
+import StickerResources
+
+public final class AnimatedStickerResourceSource: AnimatedStickerNodeSource {
+ public let account: Account
+ public let resource: MediaResource
+ public let fitzModifier: EmojiFitzModifier?
+
+ public init(account: Account, resource: MediaResource, fitzModifier: EmojiFitzModifier? = nil) {
+ self.account = account
+ self.resource = resource
+ self.fitzModifier = fitzModifier
+ }
+
+ public func cachedDataPath(width: Int, height: Int) -> Signal {
+ return chatMessageAnimationData(postbox: self.account.postbox, resource: self.resource, fitzModifier: self.fitzModifier, width: width, height: height, synchronousLoad: false)
+ |> filter { data in
+ return data.complete
+ }
+ |> map { data -> String in
+ return data.path
+ }
+ }
+
+ public func directDataPath() -> Signal {
+ return self.account.postbox.mediaBox.resourceData(resource)
+ |> filter { data in
+ return data.complete
+ }
+ |> map { data -> String in
+ return data.path
+ }
+ }
+}
diff --git a/submodules/TelegramBaseController/BUCK b/submodules/TelegramBaseController/BUCK
index 794ea2c3cc..c9bdcf90c8 100644
--- a/submodules/TelegramBaseController/BUCK
+++ b/submodules/TelegramBaseController/BUCK
@@ -18,6 +18,7 @@ static_library(
"//submodules/LegacyComponents:LegacyComponents",
"//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/PresentationDataUtils:PresentationDataUtils",
+ "//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift
index 7a7557828e..c5f1ee8aa5 100644
--- a/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift
+++ b/submodules/TelegramBaseController/Sources/LocationBroadcastNavigationAccessoryPanel.swift
@@ -6,6 +6,7 @@ import TelegramCore
import Postbox
import TelegramPresentationData
import TextFormat
+import Markdown
private let titleFont = Font.regular(12.0)
private let subtitleFont = Font.regular(10.0)
diff --git a/submodules/TelegramCore/TelegramCore/Wallets.swift b/submodules/TelegramCore/TelegramCore/Wallets.swift
index cc89ab1d2b..89a6c35f35 100644
--- a/submodules/TelegramCore/TelegramCore/Wallets.swift
+++ b/submodules/TelegramCore/TelegramCore/Wallets.swift
@@ -11,1077 +11,6 @@ import MtProtoKit
import TelegramApi
#endif
-public struct TonKeychainEncryptedData: Codable, Equatable {
- public let publicKey: Data
- public let data: Data
-
- public init(publicKey: Data, data: Data) {
- self.publicKey = publicKey
- self.data = data
- }
-}
-
-public enum TonKeychainEncryptDataError {
- case generic
-}
-
-public enum TonKeychainDecryptDataError {
- case generic
- case publicKeyMismatch
- case cancelled
-}
-
-public struct TonKeychain {
- public let encryptionPublicKey: () -> Signal
- public let encrypt: (Data) -> Signal
- public let decrypt: (TonKeychainEncryptedData) -> Signal
-
- public init(encryptionPublicKey: @escaping () -> Signal, encrypt: @escaping (Data) -> Signal, decrypt: @escaping (TonKeychainEncryptedData) -> Signal) {
- self.encryptionPublicKey = encryptionPublicKey
- self.encrypt = encrypt
- self.decrypt = decrypt
- }
-}
-
-private final class TonInstanceImpl {
- private let queue: Queue
- private let basePath: String
- private let config: String
- private let blockchainName: String
- private let network: Network?
- private var instance: TON?
-
- init(queue: Queue, basePath: String, config: String, blockchainName: String, network: Network?) {
- self.queue = queue
- self.basePath = basePath
- self.config = config
- self.blockchainName = blockchainName
- self.network = network
- }
-
- func withInstance(_ f: (TON) -> Void) {
- let instance: TON
- if let current = self.instance {
- instance = current
- } else {
- let network = self.network
- instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
- if let network = network {
- Logger.shared.log("TON Proxy", "request: \(request.data.count)")
- let _ = (
- network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: request.data)))
- |> timeout(20.0, queue: .concurrentDefaultQueue(), alternate: .fail(MTRpcError(errorCode: 500, errorDescription: "NETWORK_ERROR")))
- ).start(next: { result in
- switch result {
- case let .liteResponse(response):
- let data = response.makeData()
- Logger.shared.log("TON Proxy", "response: \(data.count)")
- request.onResult(data, nil)
- }
- }, error: { error in
- request.onResult(nil, error.errorDescription)
- })
- } else {
- request.onResult(nil, "NETWORK_DISABLED")
- }
- }, enableExternalRequests: network != nil)
- self.instance = instance
- }
- f(instance)
- }
-}
-
-public final class TonInstance {
- private let queue: Queue
- private let impl: QueueLocalObject
-
- public init(basePath: String, config: String, blockchainName: String, network: Network?) {
- self.queue = .mainQueue()
- let queue = self.queue
- self.impl = QueueLocalObject(queue: queue, generate: {
- return TonInstanceImpl(queue: queue, basePath: basePath, config: config, blockchainName: blockchainName, network: network)
- })
- }
-
- fileprivate func exportKey(key: TONKey, localPassword: Data) -> Signal<[String], NoError> {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.export(key, localPassword: localPassword).start(next: { wordList in
- guard let wordList = wordList as? [String] else {
- assertionFailure()
- return
- }
- subscriber.putNext(wordList)
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func createWallet(keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
- return Signal { subscriber in
- let disposable = MetaDisposable()
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.createKey(withLocalPassword: localPassword, mnemonicPassword: Data()).start(next: { key in
- guard let key = key as? TONKey else {
- assertionFailure()
- return
- }
- let cancel = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
- let _ = self.exportKey(key: key, localPassword: localPassword).start(next: { wordList in
- subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData), wordList))
- subscriber.putCompletion()
- }, error: { error in
- subscriber.putError(.generic)
- })
- }, error: { _ in
- subscriber.putError(.generic)
- }, completed: {
- })
- }, error: { _ in
- }, completed: {
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func importWallet(keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.importKey(withLocalPassword: localPassword, mnemonicPassword: Data(), wordList: wordList).start(next: { key in
- guard let key = key as? TONKey else {
- subscriber.putError(.generic)
- return
- }
- let cancel = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
- subscriber.putNext(WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData))
- subscriber.putCompletion()
- }, error: { _ in
- subscriber.putError(.generic)
- }, completed: {
- })
- }, error: { _ in
- subscriber.putError(.generic)
- }, completed: {
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func walletAddress(publicKey: WalletPublicKey) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.getWalletAccountAddress(withPublicKey: publicKey.rawValue).start(next: { address in
- guard let address = address as? String else {
- return
- }
- subscriber.putNext(address)
- subscriber.putCompletion()
- }, error: { _ in
- }, completed: {
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- private func getWalletStateRaw(address: String) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.getAccountState(withAddress: address).start(next: { state in
- guard let state = state as? TONAccountState else {
- return
- }
- subscriber.putNext(state)
- }, error: { error in
- if let error = error as? TONError {
- if error.text.hasPrefix("LITE_SERVER_") {
- subscriber.putError(.network)
- } else {
- subscriber.putError(.generic)
- }
- } else {
- subscriber.putError(.generic)
- }
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func getWalletState(address: String) -> Signal<(WalletState, Int64), GetWalletStateError> {
- return self.getWalletStateRaw(address: address)
- |> map { state in
- return (WalletState(balance: state.balance, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))), state.syncUtime)
- }
- }
-
- fileprivate func walletLastTransactionId(address: String) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.getAccountState(withAddress: address).start(next: { state in
- guard let state = state as? TONAccountState else {
- subscriber.putNext(nil)
- return
- }
- subscriber.putNext(state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:)))
- }, error: { error in
- if let error = error as? TONError {
- if error.text.hasPrefix("ДITE_SERVER_") {
- subscriber.putError(.network)
- } else {
- subscriber.putError(.generic)
- }
- } else {
- subscriber.putError(.generic)
- }
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func getWalletTransactions(address: String, previousId: WalletTransactionId) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.getTransactionList(withAddress: address, lt: previousId.lt, hash: previousId.transactionHash).start(next: { transactions in
- guard let transactions = transactions as? [TONTransaction] else {
- subscriber.putError(.generic)
- return
- }
- subscriber.putNext(transactions.map(WalletTransaction.init(tonTransaction:)))
- }, error: { error in
- if let error = error as? TONError {
- if error.text.hasPrefix("LITE_SERVER_") {
- subscriber.putError(.network)
- } else {
- subscriber.putError(.generic)
- }
- } else {
- subscriber.putError(.generic)
- }
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func sendGramsFromWallet(decryptedSecret: Data, localPassword: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal {
- let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.sendGrams(from: key, localPassword: localPassword, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId).start(next: { result in
- guard let result = result as? TONSendGramsResult else {
- subscriber.putError(.generic)
- return
- }
- subscriber.putNext(PendingWalletTransaction(timestamp: Int64(Date().timeIntervalSince1970), validUntilTimestamp: result.sentUntil, bodyHash: result.bodyHash, address: toAddress, value: amount, comment: textMessage))
- subscriber.putCompletion()
- }, error: { error in
- if let error = error as? TONError {
- if error.text.hasPrefix("INVALID_ACCOUNT_ADDRESS") {
- subscriber.putError(.invalidAddress)
- } else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
- subscriber.putError(.destinationIsNotInitialized)
- } else if error.text.hasPrefix("MESSAGE_TOO_LONG") {
- subscriber.putError(.messageTooLong)
- } else if error.text.hasPrefix("NOT_ENOUGH_FUNDS") {
- subscriber.putError(.notEnoughFunds)
- } else if error.text.hasPrefix("LITE_SERVER_") {
- subscriber.putError(.network)
- } else {
- subscriber.putError(.generic)
- }
- } else {
- subscriber.putError(.generic)
- }
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func walletRestoreWords(publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.export(TONKey(publicKey: publicKey.rawValue, secret: decryptedSecret), localPassword: localPassword).start(next: { wordList in
- guard let wordList = wordList as? [String] else {
- subscriber.putError(.generic)
- return
- }
- subscriber.putNext(wordList)
- }, error: { _ in
- subscriber.putError(.generic)
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
-
- fileprivate func deleteAllLocalWalletsData() -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- let cancel = ton.deleteAllKeys().start(next: { _ in
- assertionFailure()
- }, error: { _ in
- subscriber.putError(.generic)
- }, completed: {
- subscriber.putCompletion()
- })
- disposable.set(ActionDisposable {
- cancel?.dispose()
- })
- }
- }
-
- return disposable
- }
- }
- fileprivate func encrypt(_ decryptedData: Data, secret: Data) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- subscriber.putNext(ton.encrypt(decryptedData, secret: secret))
- subscriber.putCompletion()
- }
- }
-
- return disposable
- }
- }
- fileprivate func decrypt(_ encryptedData: Data, secret: Data) -> Signal {
- return Signal { subscriber in
- let disposable = MetaDisposable()
-
- self.impl.with { impl in
- impl.withInstance { ton in
- subscriber.putNext(ton.decrypt(encryptedData, secret: secret))
- subscriber.putCompletion()
- }
- }
-
- return disposable
- }
- }
-}
-
-public struct WalletPublicKey: Codable, Hashable {
- public var rawValue: String
-
- public init(rawValue: String) {
- self.rawValue = rawValue
- }
-}
-
-public struct WalletInfo: PostboxCoding, Codable, Equatable {
- public let publicKey: WalletPublicKey
- public let encryptedSecret: TonKeychainEncryptedData
-
- public init(publicKey: WalletPublicKey, encryptedSecret: TonKeychainEncryptedData) {
- self.publicKey = publicKey
- self.encryptedSecret = encryptedSecret
- }
-
- public init(decoder: PostboxDecoder) {
- self.publicKey = WalletPublicKey(rawValue: decoder.decodeStringForKey("publicKey", orElse: ""))
- if let publicKey = decoder.decodeDataForKey("encryptedSecretPublicKey"), let secret = decoder.decodeDataForKey("encryptedSecretData") {
- self.encryptedSecret = TonKeychainEncryptedData(publicKey: publicKey, data: secret)
- } else {
- self.encryptedSecret = TonKeychainEncryptedData(publicKey: Data(), data: Data())
- }
- }
-
- public func encode(_ encoder: PostboxEncoder) {
- encoder.encodeString(self.publicKey.rawValue, forKey: "publicKey")
- encoder.encodeData(self.encryptedSecret.publicKey, forKey: "encryptedSecretPublicKey")
- encoder.encodeData(self.encryptedSecret.data, forKey: "encryptedSecretData")
- }
-}
-
-public struct CombinedWalletState: Codable, Equatable {
- public var walletState: WalletState
- public var timestamp: Int64
- public var topTransactions: [WalletTransaction]
- public var pendingTransactions: [PendingWalletTransaction]
-}
-
-public struct WalletStateRecord: PostboxCoding, Equatable {
- public let info: WalletInfo
- public var exportCompleted: Bool
- public var state: CombinedWalletState?
-
- public init(info: WalletInfo, exportCompleted: Bool, state: CombinedWalletState?) {
- self.info = info
- self.exportCompleted = exportCompleted
- self.state = state
- }
-
- public init(decoder: PostboxDecoder) {
- self.info = decoder.decodeDataForKey("info").flatMap { data in
- return try? JSONDecoder().decode(WalletInfo.self, from: data)
- } ?? WalletInfo(publicKey: WalletPublicKey(rawValue: ""), encryptedSecret: TonKeychainEncryptedData(publicKey: Data(), data: Data()))
- self.exportCompleted = decoder.decodeInt32ForKey("exportCompleted", orElse: 0) != 0
- self.state = decoder.decodeDataForKey("state").flatMap { data in
- return try? JSONDecoder().decode(CombinedWalletState.self, from: data)
- }
- }
-
- public func encode(_ encoder: PostboxEncoder) {
- if let data = try? JSONEncoder().encode(self.info) {
- encoder.encodeData(data, forKey: "info")
- }
- encoder.encodeInt32(self.exportCompleted ? 1 : 0, forKey: "exportCompleted")
- if let state = self.state, let data = try? JSONEncoder().encode(state) {
- encoder.encodeData(data, forKey: "state")
- } else {
- encoder.encodeNil(forKey: "state")
- }
- }
-}
-
-public struct WalletCollection: PreferencesEntry {
- public var wallets: [WalletStateRecord]
-
- public init(wallets: [WalletStateRecord]) {
- self.wallets = wallets
- }
-
- public init(decoder: PostboxDecoder) {
- var wallets: [WalletStateRecord] = decoder.decodeObjectArrayWithDecoderForKey("wallets")
- for i in (0 ..< wallets.count).reversed() {
- if wallets[i].info.publicKey.rawValue.isEmpty {
- wallets.remove(at: i)
- }
- }
- self.wallets = wallets
- }
-
- public func encode(_ encoder: PostboxEncoder) {
- encoder.encodeObjectArray(self.wallets, forKey: "wallets")
- }
-
- public func isEqual(to: PreferencesEntry) -> Bool {
- guard let other = to as? WalletCollection else {
- return false
- }
- if self.wallets != other.wallets {
- return false
- }
- return true
- }
-}
-
-public func availableWallets(postbox: Postbox) -> Signal {
- return postbox.transaction { transaction -> WalletCollection in
- return (transaction.getPreferencesEntry(key: PreferencesKeys.walletCollection) as? WalletCollection) ?? WalletCollection(wallets: [])
- }
-}
-
-public enum CreateWalletError {
- case generic
-}
-
-public func tonlibEncrypt(tonInstance: TonInstance, decryptedData: Data, secret: Data) -> Signal {
- return tonInstance.encrypt(decryptedData, secret: secret)
-}
-public func tonlibDecrypt(tonInstance: TonInstance, encryptedData: Data, secret: Data) -> Signal {
- return tonInstance.decrypt(encryptedData, secret: secret)
-}
-
-public func createWallet(postbox: Postbox, tonInstance: TonInstance, keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
- return tonInstance.createWallet(keychain: keychain, localPassword: localPassword)
- |> mapToSignal { walletInfo, wordList -> Signal<(WalletInfo, [String]), CreateWalletError> in
- return postbox.transaction { transaction -> (WalletInfo, [String]) in
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
- walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: false, state: nil)]
- return walletCollection
- })
- return (walletInfo, wordList)
- }
- |> castError(CreateWalletError.self)
- }
-}
-
-public func confirmWalletExported(postbox: Postbox, walletInfo: WalletInfo) -> Signal {
- return postbox.transaction { transaction -> Void in
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
- for i in 0 ..< walletCollection.wallets.count {
- if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey {
- walletCollection.wallets[i].exportCompleted = true
- }
- }
- return walletCollection
- })
- }
- |> ignoreValues
-}
-
-private enum ImportWalletInternalError {
- case generic
-}
-
-public enum ImportWalletError {
- case generic
-}
-
-public func importWallet(postbox: Postbox, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal {
- return tonInstance.importWallet(keychain: keychain, wordList: wordList, localPassword: localPassword)
- |> `catch` { error -> Signal in
- switch error {
- case .generic:
- return .fail(.generic)
- }
- }
- |> mapToSignal { walletInfo -> Signal in
- return postbox.transaction { transaction -> WalletInfo in
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
- walletCollection.wallets = [WalletStateRecord(info: walletInfo, exportCompleted: true, state: nil)]
- return walletCollection
- })
- return walletInfo
- }
- |> castError(ImportWalletError.self)
- }
-}
-
-public enum DeleteAllLocalWalletsDataError {
- case generic
-}
-
-public func deleteAllLocalWalletsData(postbox: Postbox, network: Network, tonInstance: TonInstance) -> Signal {
- return tonInstance.deleteAllLocalWalletsData()
- |> then(
- postbox.transaction { transaction -> Void in
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- let walletCollection = WalletCollection(wallets: [])
- return walletCollection
- })
- }
- |> castError(DeleteAllLocalWalletsDataError.self)
- |> ignoreValues
- )
-}
-
-public enum WalletRestoreWordsError {
- case generic
-}
-
-public func walletRestoreWords(tonInstance: TonInstance, publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
- return tonInstance.walletRestoreWords(publicKey: publicKey, decryptedSecret: decryptedSecret, localPassword: localPassword)
-}
-
-public struct WalletState: Codable, Equatable {
- public let balance: Int64
- public let lastTransactionId: WalletTransactionId?
-
- public init(balance: Int64, lastTransactionId: WalletTransactionId?) {
- self.balance = balance
- self.lastTransactionId = lastTransactionId
- }
-}
-
-public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance) -> Signal {
- return tonInstance.walletAddress(publicKey: publicKey)
-}
-
-private enum GetWalletStateError {
- case generic
- case network
-}
-
-private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), GetWalletStateError> {
- return tonInstance.getWalletState(address: address)
-}
-
-public enum GetCombinedWalletStateError {
- case generic
- case network
-}
-
-public enum CombinedWalletStateResult {
- case cached(CombinedWalletState?)
- case updated(CombinedWalletState)
-}
-
-public enum CombinedWalletStateSubject {
- case wallet(WalletInfo)
- case address(String)
-}
-
-public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStateSubject, tonInstance: TonInstance, onlyCached: Bool = false) -> Signal {
- switch subject {
- case let .wallet(walletInfo):
- return postbox.transaction { transaction -> CombinedWalletState? in
- let walletCollection = (transaction.getPreferencesEntry(key: PreferencesKeys.walletCollection) as? WalletCollection) ?? WalletCollection(wallets: [])
- for item in walletCollection.wallets {
- if item.info.publicKey == walletInfo.publicKey {
- return item.state
- }
- }
- return nil
- }
- |> castError(GetCombinedWalletStateError.self)
- |> mapToSignal { cachedState -> Signal in
- if onlyCached {
- return .single(.cached(cachedState))
- }
- return .single(.cached(cachedState))
- |> then(
- tonInstance.walletAddress(publicKey: walletInfo.publicKey)
- |> castError(GetCombinedWalletStateError.self)
- |> mapToSignal { address -> Signal in
- return getWalletState(address: address, tonInstance: tonInstance)
- |> retryTonRequest(isNetworkError: { error in
- if case .network = error {
- return true
- } else {
- return false
- }
- })
- |> mapError { error -> GetCombinedWalletStateError in
- if case .network = error {
- return .network
- } else {
- return .generic
- }
- }
- |> mapToSignal { walletState, syncUtime -> Signal in
- let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
- if walletState.lastTransactionId == cachedState?.walletState.lastTransactionId {
- topTransactions = .single(cachedState?.topTransactions ?? [])
- } else {
- topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
- |> mapError { error -> GetCombinedWalletStateError in
- if case .network = error {
- return .network
- } else {
- return .generic
- }
- }
- }
- return topTransactions
- |> mapToSignal { topTransactions -> Signal in
- let lastTransactionTimestamp = topTransactions.last?.timestamp
- var listTransactionBodyHashes = Set()
- for transaction in topTransactions {
- if let message = transaction.inMessage {
- listTransactionBodyHashes.insert(message.bodyHash)
- }
- for message in transaction.outMessages {
- listTransactionBodyHashes.insert(message.bodyHash)
- }
- }
- let pendingTransactions = (cachedState?.pendingTransactions ?? []).filter { transaction in
- if transaction.validUntilTimestamp <= syncUtime {
- return false
- } else if let lastTransactionTimestamp = lastTransactionTimestamp, transaction.validUntilTimestamp <= lastTransactionTimestamp {
- return false
- } else {
- if listTransactionBodyHashes.contains(transaction.bodyHash) {
- return false
- }
- return true
- }
- }
- let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: pendingTransactions)
- return postbox.transaction { transaction -> CombinedWalletStateResult in
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
- for i in 0 ..< walletCollection.wallets.count {
- if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey {
- walletCollection.wallets[i].state = combinedState
- }
- }
- return walletCollection
- })
- return .updated(combinedState)
- }
- |> castError(GetCombinedWalletStateError.self)
- }
- }
- }
- )
- }
- case let .address(address):
- let updated = getWalletState(address: address, tonInstance: tonInstance)
- |> mapError { _ -> GetCombinedWalletStateError in
- return .generic
- }
- |> mapToSignal { walletState, syncUtime -> Signal in
- let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
-
- topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
- |> mapError { _ -> GetCombinedWalletStateError in
- return .generic
- }
- return topTransactions
- |> mapToSignal { topTransactions -> Signal in
- let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: [])
- return .single(.updated(combinedState))
- }
- }
- return .single(.cached(nil))
- |> then(updated)
- }
-}
-
-public enum SendGramsFromWalletError {
- case generic
- case secretDecryptionFailed
- case invalidAddress
- case destinationIsNotInitialized
- case messageTooLong
- case notEnoughFunds
- case network
-}
-
-public func sendGramsFromWallet(postbox: Postbox, network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> {
- return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
- |> castError(SendGramsFromWalletError.self)
- |> mapToSignal { fromAddress -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
- return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
- |> mapToSignal { result -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
- return postbox.transaction { transaction -> [PendingWalletTransaction] in
- var updatedPendingTransactions: [PendingWalletTransaction] = []
- transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
- var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
- for i in 0 ..< walletCollection.wallets.count {
- if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey {
- if var state = walletCollection.wallets[i].state {
- state.pendingTransactions.insert(result, at: 0)
- walletCollection.wallets[i].state = state
- updatedPendingTransactions = state.pendingTransactions
- }
- }
- }
- return walletCollection
- })
- return updatedPendingTransactions
- }
- |> castError(SendGramsFromWalletError.self)
- }
- }
-}
-
-public struct WalletTransactionId: Codable, Hashable {
- public var lt: Int64
- public var transactionHash: Data
-}
-
-private extension WalletTransactionId {
- init(tonTransactionId: TONTransactionId) {
- self.lt = tonTransactionId.lt
- self.transactionHash = tonTransactionId.transactionHash
- }
-}
-
-public final class WalletTransactionMessage: Codable, Equatable {
- public let value: Int64
- public let source: String
- public let destination: String
- public let textMessage: String
- public let bodyHash: Data
-
- init(value: Int64, source: String, destination: String, textMessage: String, bodyHash: Data) {
- self.value = value
- self.source = source
- self.destination = destination
- self.textMessage = textMessage
- self.bodyHash = bodyHash
- }
-
- public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
- if lhs.value != rhs.value {
- return false
- }
- if lhs.source != rhs.source {
- return false
- }
- if lhs.destination != rhs.destination {
- return false
- }
- if lhs.textMessage != rhs.textMessage {
- return false
- }
- if lhs.bodyHash != rhs.bodyHash {
- return false
- }
- return true
- }
-}
-
-private extension WalletTransactionMessage {
- convenience init(tonTransactionMessage: TONTransactionMessage) {
- self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination, textMessage: tonTransactionMessage.textMessage, bodyHash: tonTransactionMessage.bodyHash)
- }
-}
-
-public final class PendingWalletTransaction: Codable, Equatable {
- public let timestamp: Int64
- public let validUntilTimestamp: Int64
- public let bodyHash: Data
- public let address: String
- public let value: Int64
- public let comment: Data
-
- public init(timestamp: Int64, validUntilTimestamp: Int64, bodyHash: Data, address: String, value: Int64, comment: Data) {
- self.timestamp = timestamp
- self.validUntilTimestamp = validUntilTimestamp
- self.bodyHash = bodyHash
- self.address = address
- self.value = value
- self.comment = comment
- }
-
- public static func ==(lhs: PendingWalletTransaction, rhs: PendingWalletTransaction) -> Bool {
- if lhs.timestamp != rhs.timestamp {
- return false
- }
- if lhs.validUntilTimestamp != rhs.validUntilTimestamp {
- return false
- }
- if lhs.bodyHash != rhs.bodyHash {
- return false
- }
- if lhs.value != rhs.value {
- return false
- }
- if lhs.comment != rhs.comment {
- return false
- }
- return true
- }
-}
-
-public final class WalletTransaction: Codable, Equatable {
- public let data: Data
- public let transactionId: WalletTransactionId
- public let timestamp: Int64
- public let storageFee: Int64
- public let otherFee: Int64
- public let inMessage: WalletTransactionMessage?
- public let outMessages: [WalletTransactionMessage]
-
- public var transferredValueWithoutFees: Int64 {
- var value: Int64 = 0
- if let inMessage = self.inMessage {
- value += inMessage.value
- }
- for message in self.outMessages {
- value -= message.value
- }
- return value
- }
-
- init(data: Data, transactionId: WalletTransactionId, timestamp: Int64, storageFee: Int64, otherFee: Int64, inMessage: WalletTransactionMessage?, outMessages: [WalletTransactionMessage]) {
- self.data = data
- self.transactionId = transactionId
- self.timestamp = timestamp
- self.storageFee = storageFee
- self.otherFee = otherFee
- self.inMessage = inMessage
- self.outMessages = outMessages
- }
-
- public static func ==(lhs: WalletTransaction, rhs: WalletTransaction) -> Bool {
- if lhs.data != rhs.data {
- return false
- }
- if lhs.transactionId != rhs.transactionId {
- return false
- }
- if lhs.timestamp != rhs.timestamp {
- return false
- }
- if lhs.storageFee != rhs.storageFee {
- return false
- }
- if lhs.otherFee != rhs.otherFee {
- return false
- }
- if lhs.inMessage != rhs.inMessage {
- return false
- }
- if lhs.outMessages != rhs.outMessages {
- return false
- }
- return true
- }
-}
-
-private extension WalletTransaction {
- convenience init(tonTransaction: TONTransaction) {
- self.init(data: tonTransaction.data, transactionId: WalletTransactionId(tonTransactionId: tonTransaction.transactionId), timestamp: tonTransaction.timestamp, storageFee: tonTransaction.storageFee, otherFee: tonTransaction.otherFee, inMessage: tonTransaction.inMessage.flatMap(WalletTransactionMessage.init(tonTransactionMessage:)), outMessages: tonTransaction.outMessages.map(WalletTransactionMessage.init(tonTransactionMessage:)))
- }
-}
-
-public enum GetWalletTransactionsError {
- case generic
- case network
-}
-
-public func getWalletTransactions(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
- return getWalletTransactionsOnce(address: address, previousId: previousId, tonInstance: tonInstance)
- |> mapToSignal { transactions in
- guard let lastTransaction = transactions.last, transactions.count >= 2 else {
- return .single(transactions)
- }
- return getWalletTransactionsOnce(address: address, previousId: lastTransaction.transactionId, tonInstance: tonInstance)
- |> map { additionalTransactions in
- var result = transactions
- var existingIds = Set(result.map { $0.transactionId })
- for transaction in additionalTransactions {
- if !existingIds.contains(transaction.transactionId) {
- existingIds.insert(transaction.transactionId)
- result.append(transaction)
- }
- }
- return result
- }
- }
-}
-
-private func retryTonRequest(isNetworkError: @escaping (E) -> Bool) -> (Signal) -> Signal {
- return { signal in
- return signal
- |> retry(retryOnError: isNetworkError, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: 3, onQueue: Queue.concurrentDefaultQueue())
- }
-}
-
-private enum WalletLastTransactionIdError {
- case generic
- case network
-}
-
-private func getWalletTransactionsOnce(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
- let previousIdValue: Signal
- if let previousId = previousId {
- previousIdValue = .single(previousId)
- } else {
- previousIdValue = tonInstance.walletLastTransactionId(address: address)
- |> retryTonRequest(isNetworkError: { error in
- if case .network = error {
- return true
- } else {
- return false
- }
- })
- |> mapError { error -> GetWalletTransactionsError in
- if case .network = error {
- return .network
- } else {
- return .generic
- }
- }
- }
- return previousIdValue
- |> mapToSignal { previousId in
- if let previousId = previousId {
- return tonInstance.getWalletTransactions(address: address, previousId: previousId)
- |> retryTonRequest(isNetworkError: { error in
- if case .network = error {
- return true
- } else {
- return false
- }
- })
- } else {
- return .single([])
- }
- }
-}
-
public enum GetServerWalletSaltError {
case generic
}
@@ -1098,3 +27,75 @@ public func getServerWalletSalt(network: Network) -> Signal Signal {
+ return network.request(Api.functions.wallet.sendLiteRequest(body: Buffer(data: data)))
+ |> mapError { error -> WalletProxyRequestError in
+ return .generic(error.errorCode, error.errorDescription)
+ }
+ |> map { result -> Data in
+ switch result {
+ case let .liteResponse(response):
+ return response.makeData()
+ }
+ }
+}
+
+public struct WalletCollection: PreferencesEntry {
+ public var wallets: [WalletCollectionItem]
+
+ public init(wallets: [WalletCollectionItem]) {
+ self.wallets = wallets
+ }
+
+ public init(decoder: PostboxDecoder) {
+ self.wallets = decoder.decodeObjectArrayWithDecoderForKey("wallets")
+ }
+
+ public func encode(_ encoder: PostboxEncoder) {
+ encoder.encodeObjectArray(self.wallets, forKey: "wallets")
+ }
+
+ public func isEqual(to: PreferencesEntry) -> Bool {
+ guard let other = to as? WalletCollection else {
+ return false
+ }
+ if self.wallets != other.wallets {
+ return false
+ }
+ return true
+ }
+}
diff --git a/submodules/TelegramPermissionsUI/BUCK b/submodules/TelegramPermissionsUI/BUCK
index b7dc3c5751..c966bce116 100644
--- a/submodules/TelegramPermissionsUI/BUCK
+++ b/submodules/TelegramPermissionsUI/BUCK
@@ -14,11 +14,13 @@ static_library(
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AccountContext:AccountContext",
"//submodules/TextFormat:TextFormat",
+ "//submodules/Markdown:Markdown",
"//submodules/TelegramPermissions:TelegramPermissions",
"//submodules/DeviceAccess:DeviceAccess",
"//submodules/PeersNearbyIconNode:PeersNearbyIconNode",
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
"//submodules/AppBundle:AppBundle",
+ "//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift
index 7741917aea..dc0c5a123e 100644
--- a/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift
+++ b/submodules/TelegramPermissionsUI/Sources/PermissionContentNode.swift
@@ -7,6 +7,8 @@ import TextFormat
import TelegramPermissions
import PeersNearbyIconNode
import SolidRoundedButtonNode
+import PresentationDataUtils
+import Markdown
public enum PermissionContentIcon {
case image(UIImage?)
diff --git a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
index 6652bb17a8..5d1721dfd9 100644
--- a/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
+++ b/submodules/TelegramPresentationData/Sources/PresentationStrings.swift
@@ -4446,566 +4446,566 @@ public final class PresentationStrings: Equatable {
public var Channel_Setup_TypePublicHelp: String { return self._s[3909]! }
public var Passport_Identity_EditInternalPassport: String { return self._s[3910]! }
public var PhotoEditor_Skip: String { return self._s[3911]! }
- public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String {
+ public func StickerPack_RemoveMaskCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func Passport_Scans(_ value: Int32) -> String {
+ public func MuteExpires_Hours(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
public func Conversation_LiveLocationMembersCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortDays(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendVideo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Contacts_ImportersCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, _1, _2)
+ return String(format: self._ps[2 * 6 + Int(form.rawValue)]!, stringValue)
}
public func LiveLocation_MenuChatsCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notification_GameScoreSelfSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_AddStickerCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedFiles(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, _0, _1)
- }
- public func ChatList_DeleteConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_StatusMembers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func MessageTimer_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessagePoll_VotedCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LastSeen_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_File(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func ForwardedContacts(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Photo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedGifs(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Map_ETAMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortWeeks(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Theme_UsersCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Call_ShortMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedPhotos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func InviteText_ContactsCountText(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortSeconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteExpires_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func LastSeen_HoursAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_StatusSubscribers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Media_ShareVideo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortHours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_SelectedMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_RemoveStickerCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_ShortSeconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func QuickSend_Photos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteFor_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func UserCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedVideos(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Generic(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_ShortMinutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Map_ETAHours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_AddMaskCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MuteExpires_Hours(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_Seconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func Media_SharePhoto(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendPhoto(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Invitation_Members(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedStickers(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Conversation_StatusOnline(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Seconds(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Notification_GameScoreExtended(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func StickerPack_StickerCount(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func MessageTimer_Months(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Video(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedAudios(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[3 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Notification_GameScoreSelfExtended(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[4 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func CreatePoll_AddMoreOptions(_ value: Int32) -> String {
+ public func ServiceMessage_GameScoreSelfExtended(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[5 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ public func Forward_ConfirmMultipleFiles(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[6 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedAudios(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[7 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ return String(format: self._ps[8 * 6 + Int(form.rawValue)]!, _1, _2)
}
- public func AttachmentMenu_SendGif(_ value: Int32) -> String {
+ public func MessageTimer_Minutes(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[9 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String {
+ public func Call_ShortSeconds(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[10 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func Watch_UserInfo_Mute(_ value: Int32) -> String {
+ public func MessageTimer_ShortHours(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[11 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, _2, _1, _3)
- }
- public func MessageTimer_Weeks(_ value: Int32) -> String {
+ public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func AttachmentMenu_SendItem(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedVideoMessages(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func SharedMedia_Link(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[12 * 6 + Int(form.rawValue)]!, stringValue)
}
public func GroupInfo_ParticipantCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[13 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func StickerPack_RemoveMaskCount(_ value: Int32) -> String {
+ public func Notifications_ExceptionMuteExpires_Minutes(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[14 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func MuteFor_Days(_ value: Int32) -> String {
+ public func StickerPack_AddMaskCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[15 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func MessageTimer_Years(_ value: Int32) -> String {
+ public func ForwardedVideoMessages(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Call_Minutes(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, _1, _2)
- }
- public func ForwardedLocations(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func ForwardedPolls(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue)
- }
- public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String {
- let form = getPluralizationForm(self.lc, value)
- let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[16 * 6 + Int(form.rawValue)]!, stringValue)
}
public func ChatList_SelectedChats(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[17 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Conversation_StatusOnline(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[18 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Photo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[19 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedGifs(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[20 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Months(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[21 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortSeconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[22 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_ContactPhoneNumberCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[23 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedPolls(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[24 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[25 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func ChatList_DeleteConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[26 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MuteFor_Hours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[27 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[28 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func PUSH_CHANNEL_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[29 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func MuteExpires_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[30 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MuteExpires_Minutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[31 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Hours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[32 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func UserCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[33 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[34 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func MessageTimer_ShortDays(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[35 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_PHOTOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[36 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func ForwardedLocations(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[37 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreSelfSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[38 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_Minutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[39 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_PollOptionCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[40 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func QuickSend_Photos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[41 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedAuthorsOthers(_ selector: Int32, _ _0: String, _ _1: String) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[42 * 6 + Int(form.rawValue)]!, _0, _1)
+ }
+ public func SharedMedia_Generic(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[43 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_StickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[44 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Conversation_StatusMembers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[45 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Watch_LastSeen_HoursAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[46 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Media_SharePhoto(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[47 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendVideo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[48 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Chat_DeleteMessagesConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[49 * 6 + Int(form.rawValue)]!, stringValue)
}
public func ServiceMessage_GameScoreExtended(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[50 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LiveLocationUpdated_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[51 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedMessages(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[52 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendItem(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[53 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MuteFor_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[54 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Seconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[55 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notifications_ExceptionMuteExpires_Hours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[56 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[57 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedContacts(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[58 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Watch_UserInfo_Mute(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[59 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notifications_ExceptionMuteExpires_Days(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[60 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Passport_Scans(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[61 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Conversation_SelectedMessages(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[62 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Invitation_Members(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[63 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_ShortMinutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[64 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedPhotos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[65 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Notifications_Exceptions(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[66 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func Notification_GameScoreSimple(_ value: Int32) -> String {
+ public func Map_ETAMinutes(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[67 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortMinutes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[68 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Media_ShareVideo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[69 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PrivacyLastSeenSettings_AddUsers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[70 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Watch_LastSeen_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[71 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func StickerPack_RemoveStickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[72 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_VIDEOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[73 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func StickerPack_AddStickerCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[74 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func LastSeen_HoursAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[75 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func InviteText_ContactsCountText(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[76 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_PHOTOS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[77 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func SharedMedia_Video(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[78 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_ROUNDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[79 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func PUSH_MESSAGE_FWDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[80 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func PasscodeSettings_FailedAttempts(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[81 * 6 + Int(form.rawValue)]!, stringValue)
}
public func Media_ShareItem(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
- return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue)
+ return String(format: self._ps[82 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_Years(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[83 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessagePoll_VotedCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[84 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ServiceMessage_GameScoreSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[85 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Contacts_ImportersCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[86 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_Link(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[87 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ServiceMessage_GameScoreSelfSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[88 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func MessageTimer_ShortWeeks(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[89 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreExtended(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[90 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedFiles(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[91 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func ForwardedVideos(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[92 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Notification_GameScoreSimple(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[93 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Call_Seconds(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[94 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGE_FWDS(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[95 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func LastSeen_MinutesAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[96 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_File(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[97 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func SharedMedia_DeleteItemsConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[98 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func CreatePoll_AddMoreOptions(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[99 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHAT_MESSAGES(_ selector: Int32, _ _2: String, _ _1: String, _ _3: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[100 * 6 + Int(form.rawValue)]!, _2, _1, _3)
+ }
+ public func ForwardedStickers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[101 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Map_ETAHours(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[102 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[103 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func AttachmentMenu_SendGif(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[104 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_CHANNEL_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[105 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func MessageTimer_Weeks(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[106 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func VoiceOver_Chat_ContactEmailCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[107 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func Theme_UsersCount(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[108 * 6 + Int(form.rawValue)]!, stringValue)
}
public func VoiceOver_Chat_PollVotes(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[109 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func PUSH_MESSAGES(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[110 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func PUSH_MESSAGE_ROUNDS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
+ let form = getPluralizationForm(self.lc, selector)
+ return String(format: self._ps[111 * 6 + Int(form.rawValue)]!, _1, _2)
+ }
+ public func Wallpaper_DeleteConfirmation(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[112 * 6 + Int(form.rawValue)]!, stringValue)
+ }
+ public func DialogList_LiveLocationChatsCount(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[113 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func PUSH_CHANNEL_MESSAGE_VIDEOS(_ selector: Int32, _ _1: String, _ _2: Int32) -> String {
- let form = getPluralizationForm(self.lc, selector)
- return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, _1, _2)
+ public func Conversation_StatusSubscribers(_ value: Int32) -> String {
+ let form = getPluralizationForm(self.lc, value)
+ let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
+ return String(format: self._ps[114 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func MuteExpires_Days(_ value: Int32) -> String {
+ public func AttachmentMenu_SendPhoto(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = presentationStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[115 * 6 + Int(form.rawValue)]!, stringValue)
diff --git a/submodules/TelegramStringFormatting/BUCK b/submodules/TelegramStringFormatting/BUCK
index 432cd2f422..a0437f7beb 100644
--- a/submodules/TelegramStringFormatting/BUCK
+++ b/submodules/TelegramStringFormatting/BUCK
@@ -12,6 +12,7 @@ static_library(
"//submodules/PlatformRestrictionMatching:PlatformRestrictionMatching",
"//submodules/LocalizedPeerData:LocalizedPeerData",
"//submodules/TextFormat:TextFormat",
+ "//submodules/Markdown:Markdown",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/AppBundle:AppBundle",
diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift
index 3fc3b9223d..21ab40a1be 100644
--- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift
+++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift
@@ -6,6 +6,7 @@ import TelegramUIPreferences
import TextFormat
import LocalizedPeerData
import Display
+import Markdown
private let titleFont = Font.regular(13.0)
private let titleBoldFont = Font.bold(13.0)
diff --git a/submodules/TelegramUI/BUCK b/submodules/TelegramUI/BUCK
index ac79a13db7..80ec5806b9 100644
--- a/submodules/TelegramUI/BUCK
+++ b/submodules/TelegramUI/BUCK
@@ -83,6 +83,8 @@ framework(
"//submodules/HorizontalPeerItem:HorizontalPeerItem",
"//submodules/CheckNode:CheckNode",
"//submodules/AnimationUI:AnimationUI",
+ "//submodules/AnimatedStickerNode:AnimatedStickerNode",
+ "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/ActionSheetPeerItem:ActionSheetPeerItem",
"//submodules/AccountContext:AccountContext",
"//submodules/ComposePollUI:ComposePollUI",
@@ -184,6 +186,8 @@ framework(
"//submodules/SegmentedControlNode:SegmentedControlNode",
"//submodules/AppBundle:AppBundle",
"//submodules/WalletUI:WalletUI",
+ "//submodules/WalletCore:WalletCore",
+ "//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TelegramUI/TelegramUI/AccountContext.swift b/submodules/TelegramUI/TelegramUI/AccountContext.swift
index ba5de0fe18..942b5d8dc6 100644
--- a/submodules/TelegramUI/TelegramUI/AccountContext.swift
+++ b/submodules/TelegramUI/TelegramUI/AccountContext.swift
@@ -9,6 +9,7 @@ import TelegramPresentationData
import AccountContext
import LiveLocationManager
import TemporaryCachedPeerDataManager
+import WalletCore
private final class DeviceSpecificContactImportContext {
let disposable = MetaDisposable()
@@ -131,6 +132,13 @@ public final class AccountContextImpl: AccountContext {
private let deviceSpecificContactImportContexts: QueueLocalObject
private var managedAppSpecificContactsDisposable: Disposable?
+ public var hasWallets: Signal {
+ return WalletStorageInterfaceImpl(postbox: self.account.postbox).getWalletRecords()
+ |> map { records in
+ return !records.isEmpty
+ }
+ }
+
public init(sharedContext: SharedAccountContextImpl, account: Account, tonContext: StoredTonContext?, limitsConfiguration: LimitsConfiguration) {
self.sharedContextImpl = sharedContext
self.account = account
diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift
index fcd70db83f..ef7508f77d 100644
--- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift
+++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift
@@ -26,6 +26,8 @@ import SettingsUI
import AppBundle
import WalletUI
import UrlHandling
+import WalletUrl
+import WalletCore
private let handleVoipNotifications = false
diff --git a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift
index e004a71736..ec90b2aa2d 100644
--- a/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift
+++ b/submodules/TelegramUI/TelegramUI/AuthorizationSequenceSignUpControllerNode.swift
@@ -4,6 +4,7 @@ import AsyncDisplayKit
import Display
import TelegramPresentationData
import TextFormat
+import Markdown
private func roundCorners(diameter: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0)
diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift
index f4d1ace892..1e36e54e2a 100644
--- a/submodules/TelegramUI/TelegramUI/ChatController.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatController.swift
@@ -48,6 +48,7 @@ import ReactionSelectionNode
import MessageReactionListUI
import AppBundle
import WalletUI
+import WalletUrl
public enum ChatControllerPeekActions {
case standard
diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift
index 2ef02fbf11..d6fafc4731 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerGridItem.swift
@@ -8,7 +8,8 @@ import Postbox
import TelegramPresentationData
import StickerResources
import AccountContext
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
enum ChatMediaInputStickerGridSectionAccessory {
case none
@@ -299,7 +300,7 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
self.didSetUpAnimationNode = true
let dimensions = item.stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- self.animationNode?.setup(resource: .resource(item.account, item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ self.animationNode?.setup(source: AnimatedStickerResourceSource(account: item.account, resource: item.stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
}
}
}
diff --git a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift
index 43d945b08d..406910bbbf 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMediaInputStickerPackItem.swift
@@ -7,8 +7,9 @@ import SwiftSignalKit
import Postbox
import TelegramPresentationData
import StickerResources
-import AnimationUI
import ItemListStickerPackItem
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
final class ChatMediaInputStickerPackItem: ListViewItem {
let account: Account
@@ -177,7 +178,7 @@ final class ChatMediaInputStickerPackItemNode: ListViewItemNode {
self.animatedStickerNode = animatedStickerNode
animatedStickerNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0)
self.addSubnode(animatedStickerNode)
- animatedStickerNode.setup(resource: .resource(account, resource), width: 80, height: 80, mode: .cached)
+ animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 80, height: 80, mode: .cached)
}
animatedStickerNode.visibility = self.visibilityStatus && loopAnimatedStickers
if let animatedStickerNode = self.animatedStickerNode {
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageActionUrlAuthController.swift b/submodules/TelegramUI/TelegramUI/ChatMessageActionUrlAuthController.swift
index 9fcf97cfdb..ce9e5af3ee 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageActionUrlAuthController.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageActionUrlAuthController.swift
@@ -9,6 +9,7 @@ import TelegramPresentationData
import CheckNode
import TextFormat
import AccountContext
+import Markdown
private let textFont = Font.regular(13.0)
private let boldTextFont = Font.semibold(13.0)
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
index 0981583fc3..d743672dd7 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageAnimatedStickerItemNode.swift
@@ -13,8 +13,10 @@ import AccountContext
import MediaResources
import StickerResources
import ContextUI
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
import Emoji
+import Markdown
private let nameFont = Font.medium(14.0)
private let inlineBotPrefixFont = Font.regular(14.0)
@@ -313,7 +315,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let file = file {
let dimensions = file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedSize = isEmoji ? dimensions.aspectFilled(CGSize(width: 384.0, height: 384.0)) : dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
- self.animationNode.setup(resource: .resource(item.context.account, file.resource), fitzModifier: fitzModifier, width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
+ self.animationNode.setup(source: AnimatedStickerResourceSource(account: item.context.account, resource: file.resource, fitzModifier: fitzModifier), width: Int(fittedSize.width), height: Int(fittedSize.height), playbackMode: playbackMode, mode: .cached)
}
}
}
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift
index 0e072557bb..30c19b83f4 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageBubbleItemNode.swift
@@ -20,6 +20,7 @@ import ReactionSelectionNode
import PersistentStringHash
import GridMessageSelectionNode
import AppBundle
+import Markdown
private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> [(Message, AnyClass)] {
var result: [(Message, AnyClass)] = []
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift
index 509546afce..a00b50d8b4 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageInstantVideoItemNode.swift
@@ -11,6 +11,7 @@ import TextFormat
import AccountContext
import LocalizedPeerData
import ContextUI
+import Markdown
private let nameFont = Font.medium(14.0)
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift
index 29fe6562bd..e2a7ebf344 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageInteractiveMediaNode.swift
@@ -16,7 +16,8 @@ import PhotoResources
import TelegramUniversalVideoContent
import TelegramStringFormatting
import GalleryUI
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
import LocalMediaResources
import WallpaperResources
@@ -731,7 +732,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
strongSelf.animatedStickerNode = animatedStickerNode
let dimensions = updatedAnimatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 384.0, height: 384.0))
- animatedStickerNode.setup(resource: .resource(context.account, updatedAnimatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: updatedAnimatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
strongSelf.insertSubnode(animatedStickerNode, aboveSubnode: strongSelf.imageNode)
animatedStickerNode.visibility = strongSelf.visibility
}
diff --git a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift
index 57d9f13a11..703f156b45 100644
--- a/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatMessageStickerItemNode.swift
@@ -10,6 +10,7 @@ import TextFormat
import AccountContext
import StickerResources
import ContextUI
+import Markdown
private let nameFont = Font.medium(14.0)
private let inlineBotPrefixFont = Font.regular(14.0)
diff --git a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift
index 9f7e591835..dce43d01f9 100644
--- a/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatScheduleTimeControllerNode.swift
@@ -9,6 +9,7 @@ import TelegramStringFormatting
import AccountContext
import ShareController
import SolidRoundedButtonNode
+import PresentationDataUtils
class ChatScheduleTimeControllerNode: ViewControllerTracingNode, UIScrollViewDelegate {
private let context: AccountContext
diff --git a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift
index b234f75dcc..026c55d013 100644
--- a/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift
+++ b/submodules/TelegramUI/TelegramUI/ChatSearchInputPanelNode.swift
@@ -54,7 +54,7 @@ final class ChatSearchInputPanelNode: ChatInputPanelNode {
self.membersButton = HighlightableButtonNode()
self.measureResultsLabel = TextNode()
self.resultsButton = HighlightableButtonNode()
- self.activityIndicator = ActivityIndicator(type: .navigationAccent(theme))
+ self.activityIndicator = ActivityIndicator(type: .navigationAccent(theme.rootController.navigationBar.buttonColor))
self.activityIndicator.isHidden = true
super.init()
diff --git a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
index f5a25fbd81..d9bcde2d84 100644
--- a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
+++ b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift
@@ -16,7 +16,7 @@ import Lottie
import MediaResources
import PhotoResources
import ImageBlur
-import AnimationUI
+import TelegramAnimatedStickerNode
import WallpaperResources
public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal {
diff --git a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift
index e3ec584cb3..5ae3c07858 100644
--- a/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift
+++ b/submodules/TelegramUI/TelegramUI/HorizontalListContextResultsChatInputPanelItem.swift
@@ -9,7 +9,8 @@ import AVFoundation
import RadialStatusNode
import StickerResources
import PhotoResources
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
final class HorizontalListContextResultsChatInputPanelItem: ListViewItem {
let account: Account
@@ -386,7 +387,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
}
let dimensions = animatedStickerFile.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- animationNode.setup(resource: .resource(item.account, animatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ animationNode.setup(source: AnimatedStickerResourceSource(account: item.account, resource: animatedStickerFile.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
}
}
diff --git a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift
index 1c7aad73b9..996aa3603e 100755
--- a/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift
+++ b/submodules/TelegramUI/TelegramUI/HorizontalStickerGridItem.swift
@@ -7,7 +7,8 @@ import AsyncDisplayKit
import Postbox
import StickerResources
import AccountContext
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
final class HorizontalStickerGridItem: GridItem {
let account: Account
@@ -111,7 +112,7 @@ final class HorizontalStickerGridItemNode: GridItemNode {
}
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- animationNode.setup(resource: .resource(account, item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else {
diff --git a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift
index 0329022ea9..1b5d3c2ee3 100644
--- a/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift
+++ b/submodules/TelegramUI/TelegramUI/MediaInputPaneTrendingItem.swift
@@ -8,7 +8,8 @@ import TelegramCore
import TelegramPresentationData
import StickerResources
import AccountContext
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
class MediaInputPaneTrendingItem: ListViewItem {
let account: Account
@@ -118,7 +119,7 @@ final class TrendingTopItemNode: ASDisplayNode {
}
let dimensions = item.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- animationNode.setup(resource: .resource(account, item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ animationNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
self.loadDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(item.file), resource: item.file.resource).start())
} else {
self.imageNode.setSignal(chatMessageSticker(account: account, file: item.file, small: true, synchronousLoad: synchronousLoads), attemptSynchronously: synchronousLoads)
diff --git a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift
index fa30bf321d..4846c7d0eb 100644
--- a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift
+++ b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift
@@ -11,7 +11,8 @@ import AccountContext
import Tuples
import StickerResources
import PhotoResources
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
private enum NotificationContentAuthorizationError {
case unauthorized
@@ -313,7 +314,7 @@ public final class NotificationViewControllerImpl {
let dimensions = fileReference.media.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 512.0, height: 512.0))
strongSelf.imageNode.setSignal(chatMessageAnimatedSticker(postbox: accountAndImage.0.postbox, file: fileReference.media, small: false, size: fittedDimensions))
- animatedStickerNode.setup(resource: .resource(accountAndImage.0, fileReference.media.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
+ animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: accountAndImage.0, resource: fileReference.media.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .direct)
animatedStickerNode.visibility = true
accountAndImage.0.network.shouldExplicitelyKeepWorkerConnections.set(.single(true))
diff --git a/submodules/TelegramUI/TelegramUI/OpenUrl.swift b/submodules/TelegramUI/TelegramUI/OpenUrl.swift
index cb6e04a8ef..5519bf9769 100644
--- a/submodules/TelegramUI/TelegramUI/OpenUrl.swift
+++ b/submodules/TelegramUI/TelegramUI/OpenUrl.swift
@@ -15,6 +15,7 @@ import UrlEscaping
import PassportUI
import UrlHandling
import WalletUI
+import WalletUrl
public struct ParsedSecureIdUrl {
public let peerId: PeerId
diff --git a/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift b/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift
index 37f441d94b..244cb01ba1 100644
--- a/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift
+++ b/submodules/TelegramUI/TelegramUI/PeerSelectionController.swift
@@ -29,7 +29,7 @@ public final class PeerSelectionControllerImpl: ViewController, PeerSelectionCon
}
if self.inProgress {
- self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(theme: self.presentationData.theme))
+ self.navigationItem.rightBarButtonItem = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.controlColor))
} else {
self.navigationItem.rightBarButtonItem = nil
}
diff --git a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping
index 75f4a93be4..e8a787649d 100644
Binary files a/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping and b/submodules/TelegramUI/TelegramUI/Resources/PresentationStrings.mapping differ
diff --git a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift
index fb4509b04a..d6788a0011 100644
--- a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift
+++ b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift
@@ -20,6 +20,7 @@ import LocalMediaResources
import OverlayStatusController
import AlertUI
import PresentationDataUtils
+import WalletCore
private enum CallStatusText: Equatable {
case none
@@ -1031,7 +1032,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return
}
let _ = (combineLatest(queue: .mainQueue(),
- availableWallets(postbox: context.account.postbox),
+ WalletStorageInterfaceImpl(postbox: context.account.postbox).getWalletRecords(),
storedContext.keychain.encryptionPublicKey(),
context.account.postbox.preferencesView(keys: [PreferencesKeys.appConfiguration])
)
@@ -1043,7 +1044,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
let tonContext = storedContext.context(config: config, blockchainName: blockchainName, enableProxy: !walletConfiguration.disableProxy)
- if wallets.wallets.isEmpty {
+ if wallets.isEmpty {
if case .send = walletContext {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let controller = textAlertController(context: context, title: presentationData.strings.Conversation_WalletRequiredTitle, text: presentationData.strings.Conversation_WalletRequiredText, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Conversation_WalletRequiredNotNow, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Conversation_WalletRequiredSetup, action: { [weak self] in
@@ -1058,8 +1059,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
}
}
} else {
- let walletInfo = wallets.wallets[0].info
- let exportCompleted = wallets.wallets[0].exportCompleted
+ let walletInfo = wallets[0].info
+ let exportCompleted = wallets[0].exportCompleted
if let currentPublicKey = currentPublicKey {
if currentPublicKey == walletInfo.encryptedSecret.publicKey {
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonContext.instance)
diff --git a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift
index ff37014a28..86881e0b42 100644
--- a/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift
+++ b/submodules/TelegramUI/TelegramUI/StickerPaneSearchStickerItem.swift
@@ -8,7 +8,8 @@ import Postbox
import TelegramPresentationData
import StickerResources
import AccountContext
-import AnimationUI
+import AnimatedStickerNode
+import TelegramAnimatedStickerNode
final class StickerPaneSearchStickerSection: GridSection {
let code: String
@@ -164,7 +165,7 @@ final class StickerPaneSearchStickerItemNode: GridItemNode {
}
let dimensions = stickerItem.file.dimensions ?? CGSize(width: 512.0, height: 512.0)
let fittedDimensions = dimensions.aspectFitted(CGSize(width: 160.0, height: 160.0))
- self.animationNode?.setup(resource: .resource(account, stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
+ self.animationNode?.setup(source: AnimatedStickerResourceSource(account: account, resource: stickerItem.file.resource), width: Int(fittedDimensions.width), height: Int(fittedDimensions.height), mode: .cached)
self.animationNode?.visibility = self.isVisibleInGrid
self.stickerFetchedDisposable.set(freeMediaFileResourceInteractiveFetched(account: account, fileReference: stickerPackFileReference(stickerItem.file), resource: stickerItem.file.resource).start())
} else {
diff --git a/submodules/TelegramUI/TelegramUI/WalletContextImpl.swift b/submodules/TelegramUI/TelegramUI/WalletContextImpl.swift
index 76406d0096..17b093f646 100644
--- a/submodules/TelegramUI/TelegramUI/WalletContextImpl.swift
+++ b/submodules/TelegramUI/TelegramUI/WalletContextImpl.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Display
import WalletUI
import Postbox
import TelegramCore
@@ -8,15 +9,98 @@ import SwiftSignalKit
import TelegramPresentationData
import ShareController
import DeviceAccess
+import PresentationDataUtils
+import WalletCore
+
+extension WalletConfiguration {
+ static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
+ if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
+ var disableProxy = false
+ if let value = data["wallet_disable_proxy"] as? String {
+ disableProxy = value != "0"
+ } else if let value = data["wallet_disable_proxy"] as? Int {
+ disableProxy = value != 0
+ }
+ return WalletConfiguration(config: config, blockchainName: blockchainName, disableProxy: disableProxy)
+ } else {
+ return .defaultValue
+ }
+ }
+}
+
+final class WalletStorageInterfaceImpl: WalletStorageInterface {
+ private let postbox: Postbox
+
+ init(postbox: Postbox) {
+ self.postbox = postbox
+ }
+
+ func watchWalletRecords() -> Signal<[WalletStateRecord], NoError> {
+ return self.postbox.preferencesView(keys: [PreferencesKeys.walletCollection])
+ |> map { view -> [WalletStateRecord] in
+ guard let walletCollection = view.values[PreferencesKeys.walletCollection] as? WalletCollection else {
+ return []
+ }
+ return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
+ do {
+ return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
+ } catch {
+ return nil
+ }
+ }
+ }
+ }
+
+ func getWalletRecords() -> Signal<[WalletStateRecord], NoError> {
+ return self.postbox.transaction { transaction -> [WalletStateRecord] in
+ guard let walletCollection = transaction.getPreferencesEntry(key: PreferencesKeys.walletCollection) as? WalletCollection else {
+ return []
+ }
+ return walletCollection.wallets.flatMap { item -> WalletStateRecord? in
+ do {
+ return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
+ } catch {
+ return nil
+ }
+ }
+ }
+ }
+
+ func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError> {
+ return self.postbox.transaction { transaction -> [WalletStateRecord] in
+ var updatedRecords: [WalletStateRecord] = []
+ transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
+ var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
+ let updatedItems = f(walletCollection.wallets.flatMap { item -> WalletStateRecord? in
+ do {
+ return WalletStateRecord(info: try JSONDecoder().decode(WalletInfo.self, from: item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap { try? JSONDecoder().decode(CombinedWalletState.self, from: $0) })
+ } catch {
+ return nil
+ }
+ })
+ walletCollection.wallets = updatedItems.flatMap { item in
+ do {
+ return WalletCollectionItem(info: try JSONEncoder().encode(item.info), exportCompleted: item.exportCompleted, state: item.state.flatMap {
+ try? JSONEncoder().encode($0)
+ })
+ } catch {
+ return nil
+ }
+ }
+ return walletCollection
+ })
+ return updatedRecords
+ }
+ }
+}
final class WalletContextImpl: WalletContext {
private let context: AccountContext
- let postbox: Postbox
- let network: Network
+ let storage: WalletStorageInterface
let tonInstance: TonInstance
let keychain: TonKeychain
- let presentationData: PresentationData
+ let presentationData: WalletPresentationData
var inForeground: Signal {
return self.context.sharedContext.applicationBindings.applicationInForeground
@@ -25,11 +109,97 @@ final class WalletContextImpl: WalletContext {
init(context: AccountContext, tonContext: TonContext) {
self.context = context
- self.postbox = self.context.account.postbox
- self.network = self.context.account.network
+ self.storage = WalletStorageInterfaceImpl(postbox: self.context.account.postbox)
+
self.tonInstance = tonContext.instance
self.keychain = tonContext.keychain
- self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
+ let presentationData = context.sharedContext.currentPresentationData.with { $0 }
+ let theme = presentationData.theme
+ let strings = presentationData.strings
+ let timeFormat: WalletTimeFormat
+ switch presentationData.dateTimeFormat.timeFormat {
+ case .military:
+ timeFormat = .military
+ case .regular:
+ timeFormat = .regular
+ }
+ let dateFormat: WalletDateFormat
+ switch presentationData.dateTimeFormat.dateFormat {
+ case .dayFirst:
+ dateFormat = .dayFirst
+ case .monthFirst:
+ dateFormat = .monthFirst
+ }
+
+ let navigationBarData = NavigationBarPresentationData(presentationData: presentationData)
+
+ self.presentationData = WalletPresentationData(
+ theme: WalletTheme(
+ info: WalletInfoTheme(
+ incomingFundsTitleColor: theme.chatList.secretTitleColor,
+ outgoingFundsTitleColor: theme.list.itemDestructiveColor
+ ), setup: WalletSetupTheme(
+ buttonFillColor: theme.list.itemCheckColors.fillColor,
+ buttonForegroundColor: theme.list.itemCheckColors.foregroundColor,
+ inputBackgroundColor: theme.actionSheet.inputBackgroundColor,
+ inputPlaceholderColor: theme.actionSheet.inputPlaceholderColor,
+ inputTextColor: theme.actionSheet.inputTextColor,
+ inputClearButtonColor: theme.actionSheet.inputClearButtonColor.withAlphaComponent(0.8)
+ ),
+ list: WalletListTheme(
+ itemPrimaryTextColor: theme.list.itemPrimaryTextColor,
+ itemSecondaryTextColor: theme.list.itemSecondaryTextColor,
+ itemPlaceholderTextColor: theme.list.itemPlaceholderTextColor,
+ itemDestructiveColor: theme.list.itemDestructiveColor,
+ itemAccentColor: theme.list.itemAccentColor,
+ itemDisabledTextColor: theme.list.itemDisabledTextColor,
+ plainBackgroundColor: theme.list.plainBackgroundColor,
+ blocksBackgroundColor: theme.list.blocksBackgroundColor,
+ itemPlainSeparatorColor: theme.list.itemPlainSeparatorColor,
+ itemBlocksBackgroundColor: theme.list.itemBlocksBackgroundColor,
+ itemBlocksSeparatorColor: theme.list.itemBlocksSeparatorColor,
+ itemHighlightedBackgroundColor: theme.list.itemHighlightedBackgroundColor,
+ sectionHeaderTextColor: theme.list.sectionHeaderTextColor,
+ freeTextColor: theme.list.freeTextColor,
+ freeTextErrorColor: theme.list.freeTextErrorColor,
+ inputClearButtonColor: theme.list.inputClearButtonColor
+ ),
+ statusBarStyle: theme.rootController.statusBarStyle.style,
+ navigationBar: navigationBarData.theme,
+ keyboardAppearance: theme.rootController.keyboardColor.keyboardAppearance,
+ alert: AlertControllerTheme(presentationTheme: theme),
+ actionSheet: ActionSheetControllerTheme(presentationTheme: theme)
+ ), strings: WalletStrings(
+ primaryComponent: WalletStringsComponent(
+ languageCode: strings.primaryComponent.languageCode,
+ localizedName: strings.primaryComponent.localizedName,
+ pluralizationRulesCode: strings.primaryComponent.pluralizationRulesCode,
+ dict: strings.primaryComponent.dict
+ ),
+ secondaryComponent: strings.secondaryComponent.flatMap { component in
+ return WalletStringsComponent(
+ languageCode: component.languageCode,
+ localizedName: component.localizedName,
+ pluralizationRulesCode: component.pluralizationRulesCode,
+ dict: component.dict
+ )
+ },
+ groupingSeparator: strings.groupingSeparator
+ ), dateTimeFormat: WalletPresentationDateTimeFormat(
+ timeFormat: timeFormat,
+ dateFormat: dateFormat,
+ dateSeparator: presentationData.dateTimeFormat.dateSeparator,
+ decimalSeparator: presentationData.dateTimeFormat.decimalSeparator,
+ groupingSeparator: presentationData.dateTimeFormat.groupingSeparator
+ )
+ )
+ }
+
+ func getServerSalt() -> Signal {
+ return getServerWalletSalt(network: self.context.account.network)
+ |> mapError { _ -> WalletContextGetServerSaltError in
+ return .generic
+ }
}
func presentNativeController(_ controller: UIViewController) {
diff --git a/submodules/TelegramUpdateUI/BUCK b/submodules/TelegramUpdateUI/BUCK
index 760c462cc2..1a6144eb08 100644
--- a/submodules/TelegramUpdateUI/BUCK
+++ b/submodules/TelegramUpdateUI/BUCK
@@ -13,6 +13,7 @@ static_library(
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/ItemListUI:ItemListUI",
+ "//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TextFormat/BUCK b/submodules/TextFormat/BUCK
index e5adc07eab..7f3a987751 100644
--- a/submodules/TextFormat/BUCK
+++ b/submodules/TextFormat/BUCK
@@ -9,6 +9,7 @@ static_library(
"//submodules/TelegramCore:TelegramCore#dynamic",
"//submodules/Display:Display#dynamic",
"//submodules/TelegramPresentationData:TelegramPresentationData",
+ "//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/TextFormat/Sources/AddFormatToStringWithRanges.swift b/submodules/TextFormat/Sources/AddFormatToStringWithRanges.swift
index f0c2058f4e..f5d8de5bb7 100644
--- a/submodules/TextFormat/Sources/AddFormatToStringWithRanges.swift
+++ b/submodules/TextFormat/Sources/AddFormatToStringWithRanges.swift
@@ -1,5 +1,6 @@
import Foundation
import UIKit
+import Markdown
public func addAttributesToStringWithRanges(_ stringWithRanges: (String, [(Int, NSRange)]), body: MarkdownAttributeSet, argumentAttributes: [Int: MarkdownAttributeSet], textAlignment: NSTextAlignment = .natural) -> NSAttributedString {
let result = NSMutableAttributedString()
diff --git a/submodules/UndoUI/BUCK b/submodules/UndoUI/BUCK
index 2c9ca54a7d..0855d5c103 100644
--- a/submodules/UndoUI/BUCK
+++ b/submodules/UndoUI/BUCK
@@ -13,8 +13,10 @@ static_library(
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TextFormat:TextFormat",
+ "//submodules/Markdown:Markdown",
"//submodules/RadialStatusNode:RadialStatusNode",
"//submodules/AnimationUI:AnimationUI",
+ "//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/AppBundle:AppBundle",
],
frameworks = [
diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
index 5975ba0bfb..b648b02e02 100644
--- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
+++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift
@@ -4,10 +4,12 @@ import AsyncDisplayKit
import Display
import SwiftSignalKit
import TelegramPresentationData
-import AnimationUI
import TextFormat
+import Markdown
import RadialStatusNode
import AppBundle
+import AnimatedStickerNode
+import AnimationUI
final class UndoOverlayControllerNode: ViewControllerTracingNode {
private let elevatedLayout: Bool
@@ -126,7 +128,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode {
self.animationNode = nil
self.animatedStickerNode = AnimatedStickerNode()
self.animatedStickerNode?.visibility = true
- self.animatedStickerNode?.setup(resource: .localFile(path), width: 100, height: 100, playbackMode: .once, mode: .direct)
+ self.animatedStickerNode?.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 100, height: 100, playbackMode: .once, mode: .direct)
let body = MarkdownAttributeSet(font: Font.regular(14.0), textColor: .white)
let bold = MarkdownAttributeSet(font: Font.semibold(14.0), textColor: .white)
diff --git a/submodules/UrlHandling/BUCK b/submodules/UrlHandling/BUCK
index 49ac625612..6023479e72 100644
--- a/submodules/UrlHandling/BUCK
+++ b/submodules/UrlHandling/BUCK
@@ -12,6 +12,7 @@ static_library(
"//submodules/MtProtoKit:MtProtoKit#shared",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
+ "//submodules/WalletUrl:WalletUrl",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift
index 0b0270935f..ae5bd673d3 100644
--- a/submodules/UrlHandling/Sources/UrlHandling.swift
+++ b/submodules/UrlHandling/Sources/UrlHandling.swift
@@ -11,6 +11,7 @@ import MtProtoKitDynamic
import TelegramPresentationData
import TelegramUIPreferences
import AccountContext
+import WalletUrl
public enum ParsedInternalPeerUrlParameter {
case botStart(String)
@@ -435,42 +436,3 @@ public func resolveInstantViewUrl(account: Account, url: String) -> Signal Bool {
- if address.count != 48 || address.rangeOfCharacter(from: invalidWalletAddressCharacters) != nil {
- return false
- }
- return true
-}
-
-public func parseWalletUrl(_ url: URL) -> ParsedWalletUrl? {
- guard url.scheme == "ton" && url.host == "transfer" else {
- return nil
- }
- var address: String?
- let path = url.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
- if isValidWalletAddress(path) {
- address = path
- }
- var amount: Int64?
- var comment: String?
- if let query = url.query, let components = URLComponents(string: "/?" + query), let queryItems = components.queryItems {
- for queryItem in queryItems {
- if let value = queryItem.value {
- if queryItem.name == "amount", !value.isEmpty, let amountValue = Int64(value) {
- amount = amountValue
- } else if queryItem.name == "text", !value.isEmpty {
- comment = value
- }
- }
- }
- }
- return address.flatMap { ParsedWalletUrl(address: $0, amount: amount, comment: comment) }
-}
diff --git a/submodules/WalletCore/BUCK b/submodules/WalletCore/BUCK
new file mode 100644
index 0000000000..6c20866fce
--- /dev/null
+++ b/submodules/WalletCore/BUCK
@@ -0,0 +1,15 @@
+load("//Config:buck_rule_macros.bzl", "static_library")
+
+static_library(
+ name = "WalletCore",
+ srcs = glob([
+ "Sources/**/*.swift",
+ ]),
+ deps = [
+ "//submodules/MtProtoKit:MtProtoKit#shared",
+ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ ],
+)
diff --git a/submodules/WalletCore/Sources/WalletCore.swift b/submodules/WalletCore/Sources/WalletCore.swift
new file mode 100644
index 0000000000..01f2131409
--- /dev/null
+++ b/submodules/WalletCore/Sources/WalletCore.swift
@@ -0,0 +1,1015 @@
+import Foundation
+#if os(macOS)
+import SwiftSignalKitMac
+import MtProtoKitMac
+#else
+import SwiftSignalKit
+import MtProtoKit
+#endif
+
+public struct TonKeychainEncryptedData: Codable, Equatable {
+ public let publicKey: Data
+ public let data: Data
+
+ public init(publicKey: Data, data: Data) {
+ self.publicKey = publicKey
+ self.data = data
+ }
+}
+
+public enum TonKeychainEncryptDataError {
+ case generic
+}
+
+public enum TonKeychainDecryptDataError {
+ case generic
+ case publicKeyMismatch
+ case cancelled
+}
+
+public struct TonKeychain {
+ public let encryptionPublicKey: () -> Signal
+ public let encrypt: (Data) -> Signal
+ public let decrypt: (TonKeychainEncryptedData) -> Signal
+
+ public init(encryptionPublicKey: @escaping () -> Signal, encrypt: @escaping (Data) -> Signal, decrypt: @escaping (TonKeychainEncryptedData) -> Signal) {
+ self.encryptionPublicKey = encryptionPublicKey
+ self.encrypt = encrypt
+ self.decrypt = decrypt
+ }
+}
+
+public enum TonNetworkProxyResult {
+ case reponse(Data)
+ case error(String)
+}
+
+public protocol TonNetworkProxy: class {
+ func request(data: Data, timeout: Double, completion: @escaping (TonNetworkProxyResult) -> Void) -> Disposable
+}
+
+private final class TonInstanceImpl {
+ private let queue: Queue
+ private let basePath: String
+ private let config: String
+ private let blockchainName: String
+ private let proxy: TonNetworkProxy?
+ private var instance: TON?
+
+ init(queue: Queue, basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
+ self.queue = queue
+ self.basePath = basePath
+ self.config = config
+ self.blockchainName = blockchainName
+ self.proxy = proxy
+ }
+
+ func withInstance(_ f: (TON) -> Void) {
+ let instance: TON
+ if let current = self.instance {
+ instance = current
+ } else {
+ let proxy = self.proxy
+ instance = TON(keystoreDirectory: self.basePath + "/ton-keystore", config: self.config, blockchainName: self.blockchainName, performExternalRequest: { request in
+ if let proxy = proxy {
+ let _ = proxy.request(data: request.data, timeout: 20.0, completion: { result in
+ switch result {
+ case let .reponse(data):
+ request.onResult(data, nil)
+ case let .error(description):
+ request.onResult(nil, description)
+ }
+ })
+ } else {
+ request.onResult(nil, "NETWORK_DISABLED")
+ }
+ }, enableExternalRequests: proxy != nil)
+ self.instance = instance
+ }
+ f(instance)
+ }
+}
+
+public final class TonInstance {
+ private let queue: Queue
+ private let impl: QueueLocalObject
+
+ public init(basePath: String, config: String, blockchainName: String, proxy: TonNetworkProxy?) {
+ self.queue = .mainQueue()
+ let queue = self.queue
+ self.impl = QueueLocalObject(queue: queue, generate: {
+ return TonInstanceImpl(queue: queue, basePath: basePath, config: config, blockchainName: blockchainName, proxy: proxy)
+ })
+ }
+
+ fileprivate func exportKey(key: TONKey, localPassword: Data) -> Signal<[String], NoError> {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.export(key, localPassword: localPassword).start(next: { wordList in
+ guard let wordList = wordList as? [String] else {
+ assertionFailure()
+ return
+ }
+ subscriber.putNext(wordList)
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func createWallet(keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.createKey(withLocalPassword: localPassword, mnemonicPassword: Data()).start(next: { key in
+ guard let key = key as? TONKey else {
+ assertionFailure()
+ return
+ }
+ let cancel = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
+ let _ = self.exportKey(key: key, localPassword: localPassword).start(next: { wordList in
+ subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData), wordList))
+ subscriber.putCompletion()
+ }, error: { error in
+ subscriber.putError(.generic)
+ })
+ }, error: { _ in
+ subscriber.putError(.generic)
+ }, completed: {
+ })
+ }, error: { _ in
+ }, completed: {
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func importWallet(keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.importKey(withLocalPassword: localPassword, mnemonicPassword: Data(), wordList: wordList).start(next: { key in
+ guard let key = key as? TONKey else {
+ subscriber.putError(.generic)
+ return
+ }
+ let cancel = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
+ subscriber.putNext(WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData))
+ subscriber.putCompletion()
+ }, error: { _ in
+ subscriber.putError(.generic)
+ }, completed: {
+ })
+ }, error: { _ in
+ subscriber.putError(.generic)
+ }, completed: {
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func walletAddress(publicKey: WalletPublicKey) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.getWalletAccountAddress(withPublicKey: publicKey.rawValue).start(next: { address in
+ guard let address = address as? String else {
+ return
+ }
+ subscriber.putNext(address)
+ subscriber.putCompletion()
+ }, error: { _ in
+ }, completed: {
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ private func getWalletStateRaw(address: String) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.getAccountState(withAddress: address).start(next: { state in
+ guard let state = state as? TONAccountState else {
+ return
+ }
+ subscriber.putNext(state)
+ }, error: { error in
+ if let error = error as? TONError {
+ if error.text.hasPrefix("LITE_SERVER_") {
+ subscriber.putError(.network)
+ } else {
+ subscriber.putError(.generic)
+ }
+ } else {
+ subscriber.putError(.generic)
+ }
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func getWalletState(address: String) -> Signal<(WalletState, Int64), GetWalletStateError> {
+ return self.getWalletStateRaw(address: address)
+ |> map { state in
+ return (WalletState(balance: state.balance, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))), state.syncUtime)
+ }
+ }
+
+ fileprivate func walletLastTransactionId(address: String) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.getAccountState(withAddress: address).start(next: { state in
+ guard let state = state as? TONAccountState else {
+ subscriber.putNext(nil)
+ return
+ }
+ subscriber.putNext(state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:)))
+ }, error: { error in
+ if let error = error as? TONError {
+ if error.text.hasPrefix("ДITE_SERVER_") {
+ subscriber.putError(.network)
+ } else {
+ subscriber.putError(.generic)
+ }
+ } else {
+ subscriber.putError(.generic)
+ }
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func getWalletTransactions(address: String, previousId: WalletTransactionId) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.getTransactionList(withAddress: address, lt: previousId.lt, hash: previousId.transactionHash).start(next: { transactions in
+ guard let transactions = transactions as? [TONTransaction] else {
+ subscriber.putError(.generic)
+ return
+ }
+ subscriber.putNext(transactions.map(WalletTransaction.init(tonTransaction:)))
+ }, error: { error in
+ if let error = error as? TONError {
+ if error.text.hasPrefix("LITE_SERVER_") {
+ subscriber.putError(.network)
+ } else {
+ subscriber.putError(.generic)
+ }
+ } else {
+ subscriber.putError(.generic)
+ }
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func sendGramsFromWallet(decryptedSecret: Data, localPassword: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal {
+ let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.sendGrams(from: key, localPassword: localPassword, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId).start(next: { result in
+ guard let result = result as? TONSendGramsResult else {
+ subscriber.putError(.generic)
+ return
+ }
+ subscriber.putNext(PendingWalletTransaction(timestamp: Int64(Date().timeIntervalSince1970), validUntilTimestamp: result.sentUntil, bodyHash: result.bodyHash, address: toAddress, value: amount, comment: textMessage))
+ subscriber.putCompletion()
+ }, error: { error in
+ if let error = error as? TONError {
+ if error.text.hasPrefix("INVALID_ACCOUNT_ADDRESS") {
+ subscriber.putError(.invalidAddress)
+ } else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
+ subscriber.putError(.destinationIsNotInitialized)
+ } else if error.text.hasPrefix("MESSAGE_TOO_LONG") {
+ subscriber.putError(.messageTooLong)
+ } else if error.text.hasPrefix("NOT_ENOUGH_FUNDS") {
+ subscriber.putError(.notEnoughFunds)
+ } else if error.text.hasPrefix("LITE_SERVER_") {
+ subscriber.putError(.network)
+ } else {
+ subscriber.putError(.generic)
+ }
+ } else {
+ subscriber.putError(.generic)
+ }
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func walletRestoreWords(publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.export(TONKey(publicKey: publicKey.rawValue, secret: decryptedSecret), localPassword: localPassword).start(next: { wordList in
+ guard let wordList = wordList as? [String] else {
+ subscriber.putError(.generic)
+ return
+ }
+ subscriber.putNext(wordList)
+ }, error: { _ in
+ subscriber.putError(.generic)
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+
+ fileprivate func deleteAllLocalWalletsData() -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ let cancel = ton.deleteAllKeys().start(next: { _ in
+ assertionFailure()
+ }, error: { _ in
+ subscriber.putError(.generic)
+ }, completed: {
+ subscriber.putCompletion()
+ })
+ disposable.set(ActionDisposable {
+ cancel?.dispose()
+ })
+ }
+ }
+
+ return disposable
+ }
+ }
+ fileprivate func encrypt(_ decryptedData: Data, secret: Data) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ subscriber.putNext(ton.encrypt(decryptedData, secret: secret))
+ subscriber.putCompletion()
+ }
+ }
+
+ return disposable
+ }
+ }
+ fileprivate func decrypt(_ encryptedData: Data, secret: Data) -> Signal {
+ return Signal { subscriber in
+ let disposable = MetaDisposable()
+
+ self.impl.with { impl in
+ impl.withInstance { ton in
+ subscriber.putNext(ton.decrypt(encryptedData, secret: secret))
+ subscriber.putCompletion()
+ }
+ }
+
+ return disposable
+ }
+ }
+}
+
+public struct WalletPublicKey: Codable, Hashable {
+ public var rawValue: String
+
+ public init(rawValue: String) {
+ self.rawValue = rawValue
+ }
+}
+
+public struct WalletInfo: Codable, Equatable {
+ public let publicKey: WalletPublicKey
+ public let encryptedSecret: TonKeychainEncryptedData
+
+ public init(publicKey: WalletPublicKey, encryptedSecret: TonKeychainEncryptedData) {
+ self.publicKey = publicKey
+ self.encryptedSecret = encryptedSecret
+ }
+}
+
+public struct CombinedWalletState: Codable, Equatable {
+ public var walletState: WalletState
+ public var timestamp: Int64
+ public var topTransactions: [WalletTransaction]
+ public var pendingTransactions: [PendingWalletTransaction]
+}
+
+public struct WalletStateRecord: Equatable {
+ public let info: WalletInfo
+ public var exportCompleted: Bool
+ public var state: CombinedWalletState?
+
+ public init(info: WalletInfo, exportCompleted: Bool, state: CombinedWalletState?) {
+ self.info = info
+ self.exportCompleted = exportCompleted
+ self.state = state
+ }
+}
+
+public enum CreateWalletError {
+ case generic
+}
+
+public func tonlibEncrypt(tonInstance: TonInstance, decryptedData: Data, secret: Data) -> Signal {
+ return tonInstance.encrypt(decryptedData, secret: secret)
+}
+public func tonlibDecrypt(tonInstance: TonInstance, encryptedData: Data, secret: Data) -> Signal {
+ return tonInstance.decrypt(encryptedData, secret: secret)
+}
+
+public func createWallet(storage: WalletStorageInterface, tonInstance: TonInstance, keychain: TonKeychain, localPassword: Data) -> Signal<(WalletInfo, [String]), CreateWalletError> {
+ return tonInstance.createWallet(keychain: keychain, localPassword: localPassword)
+ |> mapToSignal { walletInfo, wordList -> Signal<(WalletInfo, [String]), CreateWalletError> in
+ return storage.updateWalletRecords({ records in
+ var records = records
+ records.append(WalletStateRecord(info: walletInfo, exportCompleted: false, state: nil))
+ return records
+ })
+ |> map { _ -> (WalletInfo, [String]) in
+ return (walletInfo, wordList)
+ }
+ |> castError(CreateWalletError.self)
+ }
+}
+
+public func confirmWalletExported(storage: WalletStorageInterface, publicKey: WalletPublicKey) -> Signal {
+ return storage.updateWalletRecords { records in
+ var records = records
+ for i in 0 ..< records.count {
+ if records[i].info.publicKey == publicKey {
+ records[i].exportCompleted = true
+ }
+ }
+ return records
+ }
+ |> ignoreValues
+}
+
+private enum ImportWalletInternalError {
+ case generic
+}
+
+public enum ImportWalletError {
+ case generic
+}
+
+public func importWallet(storage: WalletStorageInterface, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal {
+ return tonInstance.importWallet(keychain: keychain, wordList: wordList, localPassword: localPassword)
+ |> `catch` { error -> Signal in
+ switch error {
+ case .generic:
+ return .fail(.generic)
+ }
+ }
+ |> mapToSignal { walletInfo -> Signal in
+ return storage.updateWalletRecords { records in
+ var records = records
+ records.append(WalletStateRecord(info: walletInfo, exportCompleted: true, state: nil))
+ return records
+ }
+ |> map { _ -> WalletInfo in
+ return walletInfo
+ }
+ |> castError(ImportWalletError.self)
+ }
+}
+
+public enum DeleteAllLocalWalletsDataError {
+ case generic
+}
+
+public func deleteAllLocalWalletsData(storage: WalletStorageInterface, tonInstance: TonInstance) -> Signal {
+ return tonInstance.deleteAllLocalWalletsData()
+ |> `catch` { _ -> Signal in
+ return .complete()
+ }
+ |> then(
+ storage.updateWalletRecords { _ in [] }
+ |> castError(DeleteAllLocalWalletsDataError.self)
+ |> ignoreValues
+ )
+}
+
+public enum WalletRestoreWordsError {
+ case generic
+}
+
+public func walletRestoreWords(tonInstance: TonInstance, publicKey: WalletPublicKey, decryptedSecret: Data, localPassword: Data) -> Signal<[String], WalletRestoreWordsError> {
+ return tonInstance.walletRestoreWords(publicKey: publicKey, decryptedSecret: decryptedSecret, localPassword: localPassword)
+}
+
+public struct WalletState: Codable, Equatable {
+ public let balance: Int64
+ public let lastTransactionId: WalletTransactionId?
+
+ public init(balance: Int64, lastTransactionId: WalletTransactionId?) {
+ self.balance = balance
+ self.lastTransactionId = lastTransactionId
+ }
+}
+
+public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance) -> Signal {
+ return tonInstance.walletAddress(publicKey: publicKey)
+}
+
+private enum GetWalletStateError {
+ case generic
+ case network
+}
+
+private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), GetWalletStateError> {
+ return tonInstance.getWalletState(address: address)
+}
+
+public enum GetCombinedWalletStateError {
+ case generic
+ case network
+}
+
+public enum CombinedWalletStateResult {
+ case cached(CombinedWalletState?)
+ case updated(CombinedWalletState)
+}
+
+public enum CombinedWalletStateSubject {
+ case wallet(WalletInfo)
+ case address(String)
+}
+
+public func getCombinedWalletState(storage: WalletStorageInterface, subject: CombinedWalletStateSubject, tonInstance: TonInstance, onlyCached: Bool = false) -> Signal {
+ switch subject {
+ case let .wallet(walletInfo):
+ return storage.getWalletRecords()
+ |> map { records -> CombinedWalletState? in
+ for item in records {
+ if item.info.publicKey == walletInfo.publicKey {
+ return item.state
+ }
+ }
+ return nil
+ }
+ |> castError(GetCombinedWalletStateError.self)
+ |> mapToSignal { cachedState -> Signal in
+ if onlyCached {
+ return .single(.cached(cachedState))
+ }
+ return .single(.cached(cachedState))
+ |> then(
+ tonInstance.walletAddress(publicKey: walletInfo.publicKey)
+ |> castError(GetCombinedWalletStateError.self)
+ |> mapToSignal { address -> Signal in
+ return getWalletState(address: address, tonInstance: tonInstance)
+ |> retryTonRequest(isNetworkError: { error in
+ if case .network = error {
+ return true
+ } else {
+ return false
+ }
+ })
+ |> mapError { error -> GetCombinedWalletStateError in
+ if case .network = error {
+ return .network
+ } else {
+ return .generic
+ }
+ }
+ |> mapToSignal { walletState, syncUtime -> Signal in
+ let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
+ if walletState.lastTransactionId == cachedState?.walletState.lastTransactionId {
+ topTransactions = .single(cachedState?.topTransactions ?? [])
+ } else {
+ topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
+ |> mapError { error -> GetCombinedWalletStateError in
+ if case .network = error {
+ return .network
+ } else {
+ return .generic
+ }
+ }
+ }
+ return topTransactions
+ |> mapToSignal { topTransactions -> Signal in
+ let lastTransactionTimestamp = topTransactions.last?.timestamp
+ var listTransactionBodyHashes = Set()
+ for transaction in topTransactions {
+ if let message = transaction.inMessage {
+ listTransactionBodyHashes.insert(message.bodyHash)
+ }
+ for message in transaction.outMessages {
+ listTransactionBodyHashes.insert(message.bodyHash)
+ }
+ }
+ let pendingTransactions = (cachedState?.pendingTransactions ?? []).filter { transaction in
+ if transaction.validUntilTimestamp <= syncUtime {
+ return false
+ } else if let lastTransactionTimestamp = lastTransactionTimestamp, transaction.validUntilTimestamp <= lastTransactionTimestamp {
+ return false
+ } else {
+ if listTransactionBodyHashes.contains(transaction.bodyHash) {
+ return false
+ }
+ return true
+ }
+ }
+ let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: pendingTransactions)
+
+ return storage.updateWalletRecords { records in
+ var records = records
+ for i in 0 ..< records.count {
+ if records[i].info.publicKey == walletInfo.publicKey {
+ records[i].state = combinedState
+ }
+ }
+ return records
+ }
+ |> map { _ -> CombinedWalletStateResult in
+ return .updated(combinedState)
+ }
+ |> castError(GetCombinedWalletStateError.self)
+ }
+ }
+ }
+ )
+ }
+ case let .address(address):
+ let updated = getWalletState(address: address, tonInstance: tonInstance)
+ |> mapError { _ -> GetCombinedWalletStateError in
+ return .generic
+ }
+ |> mapToSignal { walletState, syncUtime -> Signal in
+ let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
+
+ topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
+ |> mapError { _ -> GetCombinedWalletStateError in
+ return .generic
+ }
+ return topTransactions
+ |> mapToSignal { topTransactions -> Signal in
+ let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: [])
+ return .single(.updated(combinedState))
+ }
+ }
+ return .single(.cached(nil))
+ |> then(updated)
+ }
+}
+
+public enum SendGramsFromWalletError {
+ case generic
+ case secretDecryptionFailed
+ case invalidAddress
+ case destinationIsNotInitialized
+ case messageTooLong
+ case notEnoughFunds
+ case network
+}
+
+public func sendGramsFromWallet(storage: WalletStorageInterface, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> {
+ return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
+ |> castError(SendGramsFromWalletError.self)
+ |> mapToSignal { fromAddress -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
+ return tonInstance.sendGramsFromWallet(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
+ |> mapToSignal { result -> Signal<[PendingWalletTransaction], SendGramsFromWalletError> in
+ return storage.updateWalletRecords { records in
+ var records = records
+ for i in 0 ..< records.count {
+ if records[i].info.publicKey == walletInfo.publicKey {
+ if var state = records[i].state {
+ state.pendingTransactions.insert(result, at: 0)
+ records[i].state = state
+ }
+ }
+ }
+ return records
+ }
+ |> map { records -> [PendingWalletTransaction] in
+ for i in 0 ..< records.count {
+ if records[i].info.publicKey == walletInfo.publicKey {
+ if let state = records[i].state {
+ return state.pendingTransactions
+ }
+ }
+ }
+ return []
+ }
+ |> castError(SendGramsFromWalletError.self)
+ }
+ }
+}
+
+public struct WalletTransactionId: Codable, Hashable {
+ public var lt: Int64
+ public var transactionHash: Data
+}
+
+private extension WalletTransactionId {
+ init(tonTransactionId: TONTransactionId) {
+ self.lt = tonTransactionId.lt
+ self.transactionHash = tonTransactionId.transactionHash
+ }
+}
+
+public final class WalletTransactionMessage: Codable, Equatable {
+ public let value: Int64
+ public let source: String
+ public let destination: String
+ public let textMessage: String
+ public let bodyHash: Data
+
+ init(value: Int64, source: String, destination: String, textMessage: String, bodyHash: Data) {
+ self.value = value
+ self.source = source
+ self.destination = destination
+ self.textMessage = textMessage
+ self.bodyHash = bodyHash
+ }
+
+ public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
+ if lhs.value != rhs.value {
+ return false
+ }
+ if lhs.source != rhs.source {
+ return false
+ }
+ if lhs.destination != rhs.destination {
+ return false
+ }
+ if lhs.textMessage != rhs.textMessage {
+ return false
+ }
+ if lhs.bodyHash != rhs.bodyHash {
+ return false
+ }
+ return true
+ }
+}
+
+private extension WalletTransactionMessage {
+ convenience init(tonTransactionMessage: TONTransactionMessage) {
+ self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination, textMessage: tonTransactionMessage.textMessage, bodyHash: tonTransactionMessage.bodyHash)
+ }
+}
+
+public final class PendingWalletTransaction: Codable, Equatable {
+ public let timestamp: Int64
+ public let validUntilTimestamp: Int64
+ public let bodyHash: Data
+ public let address: String
+ public let value: Int64
+ public let comment: Data
+
+ public init(timestamp: Int64, validUntilTimestamp: Int64, bodyHash: Data, address: String, value: Int64, comment: Data) {
+ self.timestamp = timestamp
+ self.validUntilTimestamp = validUntilTimestamp
+ self.bodyHash = bodyHash
+ self.address = address
+ self.value = value
+ self.comment = comment
+ }
+
+ public static func ==(lhs: PendingWalletTransaction, rhs: PendingWalletTransaction) -> Bool {
+ if lhs.timestamp != rhs.timestamp {
+ return false
+ }
+ if lhs.validUntilTimestamp != rhs.validUntilTimestamp {
+ return false
+ }
+ if lhs.bodyHash != rhs.bodyHash {
+ return false
+ }
+ if lhs.value != rhs.value {
+ return false
+ }
+ if lhs.comment != rhs.comment {
+ return false
+ }
+ return true
+ }
+}
+
+public final class WalletTransaction: Codable, Equatable {
+ public let data: Data
+ public let transactionId: WalletTransactionId
+ public let timestamp: Int64
+ public let storageFee: Int64
+ public let otherFee: Int64
+ public let inMessage: WalletTransactionMessage?
+ public let outMessages: [WalletTransactionMessage]
+
+ public var transferredValueWithoutFees: Int64 {
+ var value: Int64 = 0
+ if let inMessage = self.inMessage {
+ value += inMessage.value
+ }
+ for message in self.outMessages {
+ value -= message.value
+ }
+ return value
+ }
+
+ init(data: Data, transactionId: WalletTransactionId, timestamp: Int64, storageFee: Int64, otherFee: Int64, inMessage: WalletTransactionMessage?, outMessages: [WalletTransactionMessage]) {
+ self.data = data
+ self.transactionId = transactionId
+ self.timestamp = timestamp
+ self.storageFee = storageFee
+ self.otherFee = otherFee
+ self.inMessage = inMessage
+ self.outMessages = outMessages
+ }
+
+ public static func ==(lhs: WalletTransaction, rhs: WalletTransaction) -> Bool {
+ if lhs.data != rhs.data {
+ return false
+ }
+ if lhs.transactionId != rhs.transactionId {
+ return false
+ }
+ if lhs.timestamp != rhs.timestamp {
+ return false
+ }
+ if lhs.storageFee != rhs.storageFee {
+ return false
+ }
+ if lhs.otherFee != rhs.otherFee {
+ return false
+ }
+ if lhs.inMessage != rhs.inMessage {
+ return false
+ }
+ if lhs.outMessages != rhs.outMessages {
+ return false
+ }
+ return true
+ }
+}
+
+private extension WalletTransaction {
+ convenience init(tonTransaction: TONTransaction) {
+ self.init(data: tonTransaction.data, transactionId: WalletTransactionId(tonTransactionId: tonTransaction.transactionId), timestamp: tonTransaction.timestamp, storageFee: tonTransaction.storageFee, otherFee: tonTransaction.otherFee, inMessage: tonTransaction.inMessage.flatMap(WalletTransactionMessage.init(tonTransactionMessage:)), outMessages: tonTransaction.outMessages.map(WalletTransactionMessage.init(tonTransactionMessage:)))
+ }
+}
+
+public enum GetWalletTransactionsError {
+ case generic
+ case network
+}
+
+public func getWalletTransactions(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
+ return getWalletTransactionsOnce(address: address, previousId: previousId, tonInstance: tonInstance)
+ |> mapToSignal { transactions in
+ guard let lastTransaction = transactions.last, transactions.count >= 2 else {
+ return .single(transactions)
+ }
+ return getWalletTransactionsOnce(address: address, previousId: lastTransaction.transactionId, tonInstance: tonInstance)
+ |> map { additionalTransactions in
+ var result = transactions
+ var existingIds = Set(result.map { $0.transactionId })
+ for transaction in additionalTransactions {
+ if !existingIds.contains(transaction.transactionId) {
+ existingIds.insert(transaction.transactionId)
+ result.append(transaction)
+ }
+ }
+ return result
+ }
+ }
+}
+
+private func retryTonRequest(isNetworkError: @escaping (E) -> Bool) -> (Signal) -> Signal {
+ return { signal in
+ return signal
+ |> retry(retryOnError: isNetworkError, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: 3, onQueue: Queue.concurrentDefaultQueue())
+ }
+}
+
+private enum WalletLastTransactionIdError {
+ case generic
+ case network
+}
+
+private func getWalletTransactionsOnce(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
+ let previousIdValue: Signal
+ if let previousId = previousId {
+ previousIdValue = .single(previousId)
+ } else {
+ previousIdValue = tonInstance.walletLastTransactionId(address: address)
+ |> retryTonRequest(isNetworkError: { error in
+ if case .network = error {
+ return true
+ } else {
+ return false
+ }
+ })
+ |> mapError { error -> GetWalletTransactionsError in
+ if case .network = error {
+ return .network
+ } else {
+ return .generic
+ }
+ }
+ }
+ return previousIdValue
+ |> mapToSignal { previousId in
+ if let previousId = previousId {
+ return tonInstance.getWalletTransactions(address: address, previousId: previousId)
+ |> retryTonRequest(isNetworkError: { error in
+ if case .network = error {
+ return true
+ } else {
+ return false
+ }
+ })
+ } else {
+ return .single([])
+ }
+ }
+}
+
+public protocol WalletStorageInterface {
+ func watchWalletRecords() -> Signal<[WalletStateRecord], NoError>
+ func getWalletRecords() -> Signal<[WalletStateRecord], NoError>
+ func updateWalletRecords(_ f: @escaping ([WalletStateRecord]) -> [WalletStateRecord]) -> Signal<[WalletStateRecord], NoError>
+}
diff --git a/submodules/WalletUI/.BUCK.swp b/submodules/WalletUI/.BUCK.swp
deleted file mode 100644
index c6e6a812f9..0000000000
Binary files a/submodules/WalletUI/.BUCK.swp and /dev/null differ
diff --git a/submodules/WalletUI/BUCK b/submodules/WalletUI/BUCK
index 1a770c2a68..670e2185ab 100644
--- a/submodules/WalletUI/BUCK
+++ b/submodules/WalletUI/BUCK
@@ -8,6 +8,14 @@ apple_resource(
visibility = ["PUBLIC"],
)
+apple_asset_catalog(
+ name = 'WalletUIAssets',
+ dirs = [
+ "Images.xcassets",
+ ],
+ visibility = ["PUBLIC"],
+)
+
static_library(
name = "WalletUI",
srcs = glob([
@@ -17,24 +25,24 @@ static_library(
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
"//submodules/Display:Display#shared",
- "//submodules/Postbox:Postbox#shared",
- "//submodules/TelegramCore:TelegramCore#shared",
"//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/AppBundle:AppBundle",
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
- "//submodules/UndoUI:UndoUI",
"//submodules/AlertUI:AlertUI",
- "//submodules/TextFormat:TextFormat",
"//submodules/Camera:Camera",
- "//submodules/PasscodeInputFieldNode:PasscodeInputFieldNode",
"//submodules/QrCode:QrCode",
"//submodules/MergeLists:MergeLists",
"//submodules/GlassButtonNode:GlassButtonNode",
- "//submodules/UrlHandling:UrlHandling",
"//submodules/UrlEscaping:UrlEscaping",
"//submodules/LocalAuth:LocalAuth",
"//submodules/ScreenCaptureDetection:ScreenCaptureDetection",
- "//submodules/AnimationUI:AnimationUI",
+ "//submodules/AnimatedStickerNode:AnimatedStickerNode",
+ "//submodules/WalletUrl:WalletUrl",
+ "//submodules/WalletCore:WalletCore",
+ "//submodules/StringPluralization:StringPluralization",
+ "//submodules/ActivityIndicator:ActivityIndicator",
+ "//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode",
+ "//submodules/Markdown:Markdown",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/submodules/WalletUI/Images.xcassets/Contents.json b/submodules/WalletUI/Images.xcassets/Contents.json
new file mode 100644
index 0000000000..da4a164c91
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/Contents.json
new file mode 100644
index 0000000000..8b3d54c512
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "gem.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/gem.pdf b/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/gem.pdf
new file mode 100644
index 0000000000..643a4750e1
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/BalanceGem.imageset/gem.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/Contents.json
new file mode 100644
index 0000000000..39f252e34f
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "ic_flash.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/ic_flash.pdf b/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/ic_flash.pdf
new file mode 100644
index 0000000000..68f60cae43
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/CameraFlashIcon.imageset/ic_flash.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/Contents.json
new file mode 100644
index 0000000000..393265de7f
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "ic_gallery (3).pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/ic_gallery (3).pdf b/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/ic_gallery (3).pdf
new file mode 100644
index 0000000000..a5b36548a8
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/CameraGalleryIcon.imageset/ic_gallery (3).pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/Contents.json
new file mode 100644
index 0000000000..38f0c81fc2
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/Contents.json
@@ -0,0 +1,9 @@
+{
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ },
+ "properties" : {
+ "provides-namespace" : true
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/Contents.json
new file mode 100644
index 0000000000..a596598c09
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "duck.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/duck.pdf b/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/duck.pdf
new file mode 100644
index 0000000000..4ee986711e
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/DuckIcon.imageset/duck.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/Contents.json
new file mode 100644
index 0000000000..31daa63843
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "NavigationSettingsIcon.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/NavigationSettingsIcon.pdf b/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/NavigationSettingsIcon.pdf
new file mode 100644
index 0000000000..4f188553a0
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/NavigationSettingsIcon.imageset/NavigationSettingsIcon.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/Contents.json
new file mode 100644
index 0000000000..ae81ff437c
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/Contents.json
@@ -0,0 +1,22 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "QrGem@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "QrGem@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png
new file mode 100644
index 0000000000..35d164f8b3
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@2x.png differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png
new file mode 100644
index 0000000000..6f2e9e6ed6
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/QrGem.imageset/QrGem@3x.png differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json
new file mode 100644
index 0000000000..9bead22d1f
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "ic_qrcode.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf b/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf
new file mode 100644
index 0000000000..cf234b8223
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/QrIcon.imageset/ic_qrcode.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Contents.json
new file mode 100644
index 0000000000..aca2fcc163
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "Group2.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Group2.pdf b/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Group2.pdf
new file mode 100644
index 0000000000..1f90a5f60c
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/ReceiveButtonIcon.imageset/Group2.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json
new file mode 100644
index 0000000000..0940c4ae6d
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "ic_walletupdate.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/ic_walletupdate.pdf b/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/ic_walletupdate.pdf
new file mode 100644
index 0000000000..62702732c7
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/RefreshIcon.imageset/ic_walletupdate.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Contents.json
new file mode 100644
index 0000000000..10de2f9a6b
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "Group.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Group.pdf b/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Group.pdf
new file mode 100644
index 0000000000..7d26197899
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/SendButtonIcon.imageset/Group.pdf differ
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/Contents.json b/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/Contents.json
new file mode 100644
index 0000000000..b5a8966f3f
--- /dev/null
+++ b/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "SmallGem.pdf"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/SmallGem.pdf b/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/SmallGem.pdf
new file mode 100644
index 0000000000..cf1ac9ab53
Binary files /dev/null and b/submodules/WalletUI/Images.xcassets/Wallet/TransactionGem.imageset/SmallGem.pdf differ
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/SendingGrams.tgs b/submodules/WalletUI/Resources/Animations/SendingGrams.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/SendingGrams.tgs
rename to submodules/WalletUI/Resources/Animations/SendingGrams.tgs
diff --git a/submodules/WalletUI/Resources/Animations/WalletApologiesAccepted.tgs b/submodules/WalletUI/Resources/Animations/WalletApologiesAccepted.tgs
new file mode 100755
index 0000000000..614c2fe0e7
Binary files /dev/null and b/submodules/WalletUI/Resources/Animations/WalletApologiesAccepted.tgs differ
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletCreated.tgs b/submodules/WalletUI/Resources/Animations/WalletCreated.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletCreated.tgs
rename to submodules/WalletUI/Resources/Animations/WalletCreated.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletDone.tgs b/submodules/WalletUI/Resources/Animations/WalletDone.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletDone.tgs
rename to submodules/WalletUI/Resources/Animations/WalletDone.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletEmpty.tgs b/submodules/WalletUI/Resources/Animations/WalletEmpty.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletEmpty.tgs
rename to submodules/WalletUI/Resources/Animations/WalletEmpty.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletIntroLoading.tgs b/submodules/WalletUI/Resources/Animations/WalletIntroLoading.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletIntroLoading.tgs
rename to submodules/WalletUI/Resources/Animations/WalletIntroLoading.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletIntroStatic.tgs b/submodules/WalletUI/Resources/Animations/WalletIntroStatic.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletIntroStatic.tgs
rename to submodules/WalletUI/Resources/Animations/WalletIntroStatic.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletKeyLock.tgs b/submodules/WalletUI/Resources/Animations/WalletKeyLock.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletKeyLock.tgs
rename to submodules/WalletUI/Resources/Animations/WalletKeyLock.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletNotAvailable.tgs b/submodules/WalletUI/Resources/Animations/WalletNotAvailable.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletNotAvailable.tgs
rename to submodules/WalletUI/Resources/Animations/WalletNotAvailable.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordCheck.tgs b/submodules/WalletUI/Resources/Animations/WalletWordCheck.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordCheck.tgs
rename to submodules/WalletUI/Resources/Animations/WalletWordCheck.tgs
diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordList.tgs b/submodules/WalletUI/Resources/Animations/WalletWordList.tgs
similarity index 100%
rename from submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordList.tgs
rename to submodules/WalletUI/Resources/Animations/WalletWordList.tgs
diff --git a/submodules/WalletUI/Resources/WalletStrings.mapping b/submodules/WalletUI/Resources/WalletStrings.mapping
index 1e50f90a2a..696b14c5b8 100644
Binary files a/submodules/WalletUI/Resources/WalletStrings.mapping and b/submodules/WalletUI/Resources/WalletStrings.mapping differ
diff --git a/submodules/WalletUI/Sources/ItemList/ItemListControllerNode.swift b/submodules/WalletUI/Sources/ItemList/ItemListControllerNode.swift
index 89661fdf44..72784e604b 100644
--- a/submodules/WalletUI/Sources/ItemList/ItemListControllerNode.swift
+++ b/submodules/WalletUI/Sources/ItemList/ItemListControllerNode.swift
@@ -3,7 +3,6 @@ import UIKit
import AsyncDisplayKit
import Display
import SwiftSignalKit
-import TelegramCore
import MergeLists
typealias ItemListSectionId = Int32
diff --git a/submodules/WalletUI/Sources/ItemList/Items/ItemListMultilineTextItem.swift b/submodules/WalletUI/Sources/ItemList/Items/ItemListMultilineTextItem.swift
index cfa233a510..93e3f4c752 100644
--- a/submodules/WalletUI/Sources/ItemList/Items/ItemListMultilineTextItem.swift
+++ b/submodules/WalletUI/Sources/ItemList/Items/ItemListMultilineTextItem.swift
@@ -3,8 +3,6 @@ import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
-import TextFormat
-import AccountContext
enum ItemListMultilineTextBaseFont {
case `default`
@@ -14,22 +12,20 @@ enum ItemListMultilineTextBaseFont {
class ItemListMultilineTextItem: ListViewItem, ItemListItem {
let theme: WalletTheme
let text: String
- let enabledEntityTypes: EnabledEntityTypes
let font: ItemListMultilineTextBaseFont
let sectionId: ItemListSectionId
let style: ItemListStyle
let action: (() -> Void)?
let longTapAction: (() -> Void)?
- let linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)?
+ let linkItemAction: ((String) -> Void)?
let tag: Any?
let selectable: Bool
- init(theme: WalletTheme, text: String, enabledEntityTypes: EnabledEntityTypes, font: ItemListMultilineTextBaseFont = .default, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, linkItemAction: ((TextLinkItemActionType, TextLinkItem) -> Void)? = nil, tag: Any? = nil) {
+ init(theme: WalletTheme, text: String, font: ItemListMultilineTextBaseFont = .default, sectionId: ItemListSectionId, style: ItemListStyle, action: (() -> Void)? = nil, longTapAction: (() -> Void)? = nil, linkItemAction: ((String) -> Void)? = nil, tag: Any? = nil) {
self.theme = theme
self.text = text
- self.enabledEntityTypes = enabledEntityTypes
self.font = font
self.sectionId = sectionId
self.style = style
@@ -198,8 +194,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
boldItalicFont = Font.semiboldItalicMonospace(17.0)
}
- let entities = generateTextEntities(item.text, enabledTypes: item.enabledEntityTypes)
- let string = stringWithAppliedEntities(item.text, entities: entities, baseColor: textColor, linkColor: item.theme.list.itemAccentColor, baseFont: baseFont, linkFont: linkFont, boldFont: boldFont, italicFont: italicFont, boldItalicFont: boldItalicFont, fixedFont: titleFixedFont, blockQuoteFont: titleFont)
+ let string = NSAttributedString(string: item.text, font: baseFont, textColor: textColor)
let (titleLayout, titleApply) = makeTextLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@@ -357,7 +352,7 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
switch gesture {
case .tap, .longTap:
if let item = self.item, let linkItem = self.linkItemAtPoint(location) {
- item.linkItemAction?(gesture == .tap ? .tap : .longTap, linkItem)
+ item.linkItemAction?(linkItem)
}
default:
break
@@ -368,18 +363,10 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
}
}
- private func linkItemAtPoint(_ point: CGPoint) -> TextLinkItem? {
+ private func linkItemAtPoint(_ point: CGPoint) -> String? {
let textNodeFrame = self.textNode.frame
if let (_, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
- if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
- return .url(url)
- } else if let peerName = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.PeerTextMention)] as? String {
- return .mention(peerName)
- } else if let hashtag = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.Hashtag)] as? TelegramHashtag {
- return .hashtag(hashtag.peerName, hashtag.hashtag)
- } else {
- return nil
- }
+ return nil
}
return nil
}
@@ -395,11 +382,6 @@ class ItemListMultilineTextItemNode: ListViewItemNode {
let textNodeFrame = self.textNode.frame
if let (index, attributes) = self.textNode.attributesAtPoint(CGPoint(x: point.x - textNodeFrame.minX, y: point.y - textNodeFrame.minY)) {
let possibleNames: [String] = [
- TelegramTextAttributes.URL,
- TelegramTextAttributes.PeerMention,
- TelegramTextAttributes.PeerTextMention,
- TelegramTextAttributes.BotCommand,
- TelegramTextAttributes.Hashtag
]
for name in possibleNames {
if let _ = attributes[NSAttributedString.Key(rawValue: name)] {
diff --git a/submodules/WalletUI/Sources/ItemList/Items/ItemListTextItem.swift b/submodules/WalletUI/Sources/ItemList/Items/ItemListTextItem.swift
index 058c960cf8..82cfe59bef 100644
--- a/submodules/WalletUI/Sources/ItemList/Items/ItemListTextItem.swift
+++ b/submodules/WalletUI/Sources/ItemList/Items/ItemListTextItem.swift
@@ -3,7 +3,7 @@ import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
-import TextFormat
+import Markdown
enum ItemListTextItemText {
case plain(String)
@@ -114,7 +114,7 @@ class ItemListTextItemNode: ListViewItemNode {
attributedText = NSAttributedString(string: text, font: titleFont, textColor: item.theme.list.freeTextColor)
case let .markdown(text):
attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: titleFont, textColor: item.theme.list.freeTextColor), bold: MarkdownAttributeSet(font: titleBoldFont, textColor: item.theme.list.freeTextColor), link: MarkdownAttributeSet(font: titleFont, textColor: item.theme.list.itemAccentColor), linkAttribute: { contents in
- return (TelegramTextAttributes.URL, contents)
+ return ("URL", contents)
}))
}
let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: attributedText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: params.width - params.rightInset - leftInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
@@ -160,7 +160,7 @@ class ItemListTextItemNode: ListViewItemNode {
let titleFrame = self.titleNode.frame
if let item = self.item, titleFrame.contains(location) {
if let (_, attributes) = self.titleNode.attributesAtPoint(CGPoint(x: location.x - titleFrame.minX, y: location.y - titleFrame.minY)) {
- if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
+ if let url = attributes[NSAttributedString.Key(rawValue: "URL")] as? String {
item.linkAction?(.tap(url))
}
}
diff --git a/submodules/WalletUI/Sources/ToastNode.swift b/submodules/WalletUI/Sources/ToastNode.swift
index e69de29bb2..8ca7d38cfd 100644
--- a/submodules/WalletUI/Sources/ToastNode.swift
+++ b/submodules/WalletUI/Sources/ToastNode.swift
@@ -0,0 +1,78 @@
+import Foundation
+import UIKit
+import Display
+import AsyncDisplayKit
+import AnimatedStickerNode
+
+final class ToastNode: ASDisplayNode {
+ private let backgroundNode: ASDisplayNode
+ private let effectView: UIView
+ private let animationNode: AnimatedStickerNode
+ private let textNode: ImmediateTextNode
+
+ init(theme: WalletTheme, animationPath: String, text: String) {
+ self.backgroundNode = ASDisplayNode()
+ self.backgroundNode.cornerRadius = 9.0
+ self.backgroundNode.clipsToBounds = true
+ if case .dark = theme.keyboardAppearance {
+ self.backgroundNode.backgroundColor = theme.navigationBar.backgroundColor
+ } else {
+ self.backgroundNode.backgroundColor = .clear
+ }
+
+ self.effectView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
+ self.backgroundNode.view.addSubview(self.effectView)
+
+ self.animationNode = AnimatedStickerNode()
+ self.animationNode.visibility = true
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: animationPath), width: 100, height: 100, playbackMode: .once, mode: .direct)
+
+ self.textNode = ImmediateTextNode()
+ self.textNode.displaysAsynchronously = false
+ self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: .white)
+ self.textNode.maximumNumberOfLines = 2
+
+ super.init()
+
+ self.addSubnode(self.backgroundNode)
+ self.addSubnode(self.animationNode)
+ self.addSubnode(self.textNode)
+ }
+
+ func update(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
+ let contentSideInset: CGFloat = 10.0
+ let contentVerticalInset: CGFloat = 8.0
+ let iconSpacing: CGFloat = 4.0
+
+ let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - contentSideInset * 2.0, height: .greatestFiniteMagnitude))
+ let iconSize = CGSize(width: 32.0, height: 32.0)
+
+ let contentSize = CGSize(width: iconSize.width + iconSpacing + textSize.width, height: max(iconSize.height, textSize.height))
+
+ let insets = layout.insets(options: .input)
+ let contentOriginX = floor((layout.size.width - contentSize.width) / 2.0)
+ let contentOriginY = insets.top + floor((layout.size.height - insets.top - insets.bottom - contentSize.height) / 2.0)
+
+ let iconFrame = CGRect(origin: CGPoint(x: contentOriginX, y: contentOriginY + floor((contentSize.height - iconSize.height) / 2.0)), size: iconSize)
+ transition.updateFrame(node: self.animationNode, frame: iconFrame)
+ self.animationNode.updateLayout(size: iconFrame.size)
+
+ let textFrame = CGRect(origin: CGPoint(x: iconFrame.maxX + iconSpacing, y: contentOriginY + floor((contentSize.height - textSize.height) / 2.0)), size: textSize)
+ transition.updateFrame(node: self.textNode, frame: textFrame)
+
+ let backgroundFrame = CGRect(origin: CGPoint(x: contentOriginX - contentSideInset, y: contentOriginY - contentVerticalInset), size: CGSize(width: contentSize.width + contentSideInset * 2.0, height: contentSize.height + contentVerticalInset * 2.0))
+ transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
+ transition.updateFrame(view: self.effectView, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
+ }
+
+ override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
+ return nil
+ }
+
+ func show(removed: @escaping () -> Void) {
+ self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
+ self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 3.0, removeOnCompletion: false, completion: { _ in
+ removed()
+ })
+ }
+}
diff --git a/submodules/WalletUI/Sources/WalletConfiguration.swift b/submodules/WalletUI/Sources/WalletConfiguration.swift
index 707a740cb5..0190db028d 100644
--- a/submodules/WalletUI/Sources/WalletConfiguration.swift
+++ b/submodules/WalletUI/Sources/WalletConfiguration.swift
@@ -1,8 +1,7 @@
import Foundation
-import TelegramCore
public struct WalletConfiguration {
- static var defaultValue: WalletConfiguration {
+ public static var defaultValue: WalletConfiguration {
return WalletConfiguration(config: nil, blockchainName: nil, disableProxy: false)
}
@@ -10,23 +9,9 @@ public struct WalletConfiguration {
public let blockchainName: String?
public let disableProxy: Bool
- fileprivate init(config: String?, blockchainName: String?, disableProxy: Bool) {
+ public init(config: String?, blockchainName: String?, disableProxy: Bool) {
self.config = config
self.blockchainName = blockchainName
self.disableProxy = disableProxy
}
-
- public static func with(appConfiguration: AppConfiguration) -> WalletConfiguration {
- if let data = appConfiguration.data, let config = data["wallet_config"] as? String, let blockchainName = data["wallet_blockchain_name"] as? String {
- var disableProxy = false
- if let value = data["wallet_disable_proxy"] as? String {
- disableProxy = value != "0"
- } else if let value = data["wallet_disable_proxy"] as? Int {
- disableProxy = value != 0
- }
- return WalletConfiguration(config: config, blockchainName: blockchainName, disableProxy: disableProxy)
- } else {
- return .defaultValue
- }
- }
}
diff --git a/submodules/WalletUI/Sources/WalletContext.swift b/submodules/WalletUI/Sources/WalletContext.swift
index 5e280e040e..caed3f475c 100644
--- a/submodules/WalletUI/Sources/WalletContext.swift
+++ b/submodules/WalletUI/Sources/WalletContext.swift
@@ -1,18 +1,22 @@
import Foundation
import UIKit
-import Postbox
-import TelegramCore
import SwiftSignalKit
+import WalletCore
+
+public enum WalletContextGetServerSaltError {
+ case generic
+}
public protocol WalletContext {
- var postbox: Postbox { get }
- var network: Network { get }
+ var storage: WalletStorageInterface { get }
var tonInstance: TonInstance { get }
var keychain: TonKeychain { get }
var presentationData: WalletPresentationData { get }
var inForeground: Signal { get }
+ func getServerSalt() -> Signal
+
func presentNativeController(_ controller: UIViewController)
func idleTimerExtension() -> Disposable
diff --git a/submodules/WalletUI/Sources/WalletCreateInvoiceScreen.swift b/submodules/WalletUI/Sources/WalletCreateInvoiceScreen.swift
index 48fc296ef9..248e90b42a 100644
--- a/submodules/WalletUI/Sources/WalletCreateInvoiceScreen.swift
+++ b/submodules/WalletUI/Sources/WalletCreateInvoiceScreen.swift
@@ -3,8 +3,6 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SwiftSignalKit
import OverlayStatusController
@@ -237,7 +235,7 @@ private enum WalletCreateInvoiceScreenEntry: ItemListNodeEntry {
case let .addressHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .address(theme, text, monospace):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], font: monospace ? .monospace : .default, sectionId: self.section, style: .blocks)
+ return ItemListMultilineTextItem(theme: theme, text: text, font: monospace ? .monospace : .default, sectionId: self.section, style: .blocks)
case let .copyAddress(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.copyAddress()
diff --git a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift
index 4636214a91..e53540b078 100644
--- a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift
+++ b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift
@@ -2,8 +2,7 @@ import Foundation
import UIKit
import Display
import AsyncDisplayKit
-import TelegramCore
-import AnimationUI
+import AnimatedStickerNode
import SwiftSignalKit
import AppBundle
@@ -68,7 +67,7 @@ final class WalletInfoEmptyItemNode: ListViewItemNode {
self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "WalletEmpty", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 280, height: 280, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 280, height: 280, playbackMode: .once, mode: .direct)
self.animationNode.visibility = true
}
diff --git a/submodules/WalletUI/Sources/WalletInfoScreen.swift b/submodules/WalletUI/Sources/WalletInfoScreen.swift
index d2d74d4310..8426eeea1d 100644
--- a/submodules/WalletUI/Sources/WalletInfoScreen.swift
+++ b/submodules/WalletUI/Sources/WalletInfoScreen.swift
@@ -3,12 +3,11 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
import SwiftSignalKit
import MergeLists
-import AnimationUI
+import AnimatedStickerNode
+import WalletCore
public final class WalletInfoScreen: ViewController {
private let context: WalletContext
@@ -69,7 +68,9 @@ public final class WalletInfoScreen: ViewController {
guard let strongSelf = self, let walletInfo = strongSelf.walletInfo else {
return
}
- strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: arc4random64(), walletInfo: walletInfo))
+ var randomId: Int64 = 0
+ arc4random_buf(&randomId, 8)
+ strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: randomId, walletInfo: walletInfo))
}, receiveAction: { [weak self] in
guard let strongSelf = self, let walletInfo = strongSelf.walletInfo else {
return
@@ -140,7 +141,7 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
self.balanceIconNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") {
- self.balanceIconNode.setup(resource: .localFile(path), width: 120, height: 120, mode: .direct)
+ self.balanceIconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 120, height: 120, mode: .direct)
self.balanceIconNode.visibility = true
}
@@ -606,12 +607,12 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
if let walletInfo = walletInfo {
subject = .wallet(walletInfo)
- self.watchCombinedStateDisposable = (context.postbox.preferencesView(keys: [PreferencesKeys.walletCollection])
- |> deliverOnMainQueue).start(next: { [weak self] view in
- guard let strongSelf = self, let wallets = view.values[PreferencesKeys.walletCollection] as? WalletCollection else {
+ self.watchCombinedStateDisposable = (context.storage.watchWalletRecords()
+ |> deliverOnMainQueue).start(next: { [weak self] records in
+ guard let strongSelf = self else {
return
}
- for wallet in wallets.wallets {
+ for wallet in records {
if wallet.info.publicKey == walletInfo.publicKey {
if let state = wallet.state {
if state.pendingTransactions != strongSelf.combinedState?.pendingTransactions || state.timestamp != strongSelf.combinedState?.timestamp {
@@ -628,7 +629,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
subject = .address(address)
}
let pollCombinedState: Signal = (
- getCombinedWalletState(postbox: context.postbox, subject: subject, tonInstance: context.tonInstance)
+ getCombinedWalletState(storage: context.storage, subject: subject, tonInstance: context.tonInstance)
|> ignoreValues
|> `catch` { _ -> Signal in
return .complete()
@@ -735,7 +736,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
subject = .address(self.address)
}
- self.stateDisposable.set((getCombinedWalletState(postbox: self.context.postbox, subject: subject, tonInstance: self.context.tonInstance)
+ self.stateDisposable.set((getCombinedWalletState(storage: self.context.storage, subject: subject, tonInstance: self.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else {
return
diff --git a/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift b/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift
index ec59ffaa74..2dcf846c7a 100644
--- a/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift
+++ b/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift
@@ -3,7 +3,6 @@ import UIKit
import Display
import AsyncDisplayKit
import SwiftSignalKit
-import TelegramCore
private let transactionIcon = UIImage(bundleImageName: "Wallet/TransactionGem")?.precomposed()
diff --git a/submodules/WalletUI/Sources/WalletPasscodeScreen.swift b/submodules/WalletUI/Sources/WalletPasscodeScreen.swift
index a288cd0178..5be1703679 100644
--- a/submodules/WalletUI/Sources/WalletPasscodeScreen.swift
+++ b/submodules/WalletUI/Sources/WalletPasscodeScreen.swift
@@ -3,8 +3,6 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import AnimationUI
import SwiftSignalKit
import OverlayStatusController
diff --git a/submodules/WalletUI/Sources/WalletQrScanScreen.swift b/submodules/WalletUI/Sources/WalletQrScanScreen.swift
index 30e2d8f88f..c2a8c72e2d 100644
--- a/submodules/WalletUI/Sources/WalletQrScanScreen.swift
+++ b/submodules/WalletUI/Sources/WalletQrScanScreen.swift
@@ -4,12 +4,11 @@ import AppBundle
import AsyncDisplayKit
import Display
import SwiftSignalKit
-import TelegramCore
import Camera
import GlassButtonNode
-import UrlHandling
import CoreImage
import AlertUI
+import WalletUrl
private func generateFrameImage() -> UIImage? {
return generateImage(CGSize(width: 64.0, height: 64.0), contextGenerator: { size, context in
diff --git a/submodules/WalletUI/Sources/WalletQrViewScreen.swift b/submodules/WalletUI/Sources/WalletQrViewScreen.swift
index 543cab2298..b083a8b4ea 100644
--- a/submodules/WalletUI/Sources/WalletQrViewScreen.swift
+++ b/submodules/WalletUI/Sources/WalletQrViewScreen.swift
@@ -4,10 +4,8 @@ import SwiftSignalKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
import QrCode
-import ShareController
-import AnimationUI
+import AnimatedStickerNode
func shareInvoiceQrCode(context: WalletContext, invoice: String) {
let _ = (qrCode(string: invoice, color: .black, backgroundColor: .white, icon: .custom(UIImage(bundleImageName: "Wallet/QrGem")))
@@ -127,7 +125,7 @@ private final class WalletQrViewScreenNode: ViewControllerTracingNode {
self.iconNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") {
- self.iconNode.setup(resource: .localFile(path), width: 240, height: 240, mode: .direct)
+ self.iconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 240, height: 240, mode: .direct)
self.iconNode.visibility = true
}
diff --git a/submodules/WalletUI/Sources/WalletReceiveScreen.swift b/submodules/WalletUI/Sources/WalletReceiveScreen.swift
index fbc47c55e9..cbf03e1f31 100644
--- a/submodules/WalletUI/Sources/WalletReceiveScreen.swift
+++ b/submodules/WalletUI/Sources/WalletReceiveScreen.swift
@@ -3,11 +3,8 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SwiftSignalKit
import OverlayStatusController
-import ShareController
private final class WalletReceiveScreenArguments {
let context: WalletContext
@@ -141,7 +138,7 @@ private enum WalletReceiveScreenEntry: ItemListNodeEntry {
case let .addressHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .address(theme, text, monospace):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], font: monospace ? .monospace : .default, sectionId: self.section, style: .blocks)
+ return ItemListMultilineTextItem(theme: theme, text: text, font: monospace ? .monospace : .default, sectionId: self.section, style: .blocks)
case let .copyAddress(theme, text):
return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: {
arguments.copyAddress()
diff --git a/submodules/WalletUI/Sources/WalletSendScreen.swift b/submodules/WalletUI/Sources/WalletSendScreen.swift
index 3883f7ac52..54f5510d2c 100644
--- a/submodules/WalletUI/Sources/WalletSendScreen.swift
+++ b/submodules/WalletUI/Sources/WalletSendScreen.swift
@@ -3,13 +3,12 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SwiftSignalKit
import AlertUI
-import TextFormat
-import UrlHandling
import OverlayStatusController
+import WalletUrl
+import WalletCore
+import Markdown
private let balanceIcon = UIImage(bundleImageName: "Wallet/TransactionGem")?.precomposed()
@@ -281,7 +280,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
}
let serverSaltValue = Promise()
- serverSaltValue.set(getServerWalletSalt(network: context.network)
+ serverSaltValue.set(context.getServerSalt()
|> map(Optional.init)
|> `catch` { _ -> Signal in
return .single(nil)
@@ -419,8 +418,8 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
}
})
- let walletState: Signal = getCombinedWalletState(postbox: context.postbox, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: true)
- |> map { combinedState in
+ let walletState: Signal = getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: true)
+ |> map { combinedState -> WalletState? in
var state: WalletState?
switch combinedState {
case let .cached(combinedState):
@@ -433,7 +432,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
|> `catch` { _ -> Signal in
return .single(nil)
|> then(
- getCombinedWalletState(postbox: context.postbox, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: false)
+ getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: false)
|> map { combinedState -> WalletState? in
var state: WalletState?
switch combinedState {
diff --git a/submodules/WalletUI/Sources/WalletSettingsScreen.swift b/submodules/WalletUI/Sources/WalletSettingsScreen.swift
index 43ab4bd2f5..cc5edd862a 100644
--- a/submodules/WalletUI/Sources/WalletSettingsScreen.swift
+++ b/submodules/WalletUI/Sources/WalletSettingsScreen.swift
@@ -3,12 +3,10 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
-import AnimationUI
import SwiftSignalKit
import OverlayStatusController
+import WalletCore
private final class WalletSettingsControllerArguments {
let exportWallet: () -> Void
@@ -104,7 +102,7 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
presentControllerImpl?(controller, nil)
let _ = (context.keychain.decrypt(walletInfo.encryptedSecret)
|> deliverOnMainQueue).start(next: { [weak controller] decryptedSecret in
- let _ = (getServerWalletSalt(network: context.network)
+ let _ = (context.getServerSalt()
|> deliverOnMainQueue).start(next: { serverSalt in
let _ = (walletRestoreWords(tonInstance: context.tonInstance, publicKey: walletInfo.publicKey, decryptedSecret: decryptedSecret, localPassword: serverSalt)
|> deliverOnMainQueue).start(next: { [weak controller] wordList in
@@ -128,7 +126,7 @@ public func walletSettingsController(context: WalletContext, walletInfo: WalletI
actionSheet?.dismissAnimated()
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
presentControllerImpl?(controller, nil)
- let _ = (deleteAllLocalWalletsData(postbox: context.postbox, network: context.network, tonInstance: context.tonInstance)
+ let _ = (deleteAllLocalWalletsData(storage: context.storage, tonInstance: context.tonInstance)
|> deliverOnMainQueue).start(error: { [weak controller] _ in
controller?.dismiss()
}, completed: { [weak controller] in
diff --git a/submodules/WalletUI/Sources/WalletSplashScreen.swift b/submodules/WalletUI/Sources/WalletSplashScreen.swift
index e08a4e820f..7266ab037f 100644
--- a/submodules/WalletUI/Sources/WalletSplashScreen.swift
+++ b/submodules/WalletUI/Sources/WalletSplashScreen.swift
@@ -3,15 +3,14 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
-import AnimationUI
import SwiftSignalKit
import OverlayStatusController
import AlertUI
-import TextFormat
import LocalAuth
+import AnimatedStickerNode
+import WalletCore
+import Markdown
public enum WalletSecureStorageResetReason {
case notAvailable
@@ -51,7 +50,7 @@ public final class WalletSplashScreen: ViewController {
self.walletCreatedPreloadState = walletCreatedPreloadState
} else {
self.walletCreatedPreloadState = Promise()
- self.walletCreatedPreloadState?.set(getCombinedWalletState(postbox: context.postbox, subject: .wallet(walletInfo), tonInstance: context.tonInstance)
+ self.walletCreatedPreloadState?.set(getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance)
|> map(Optional.init)
|> `catch` { _ -> Signal in
return .single(nil)
@@ -62,7 +61,7 @@ public final class WalletSplashScreen: ViewController {
self.walletCreatedPreloadState = walletCreatedPreloadState
} else {
self.walletCreatedPreloadState = Promise()
- self.walletCreatedPreloadState?.set(getCombinedWalletState(postbox: context.postbox, subject: .wallet(walletInfo), tonInstance: context.tonInstance)
+ self.walletCreatedPreloadState?.set(getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance)
|> map(Optional.init)
|> `catch` { _ -> Signal in
return .single(nil)
@@ -131,7 +130,7 @@ public final class WalletSplashScreen: ViewController {
}
private func sendGrams(walletInfo: WalletInfo, decryptedSecret: Data, address: String, amount: Int64, textMessage: Data, forceIfDestinationNotInitialized: Bool, randomId: Int64, serverSalt: Data) {
- let _ = (sendGramsFromWallet(postbox: self.context.postbox, network: self.context.network, tonInstance: self.context.tonInstance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, localPassword: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
+ let _ = (sendGramsFromWallet(storage: self.context.storage, tonInstance: self.context.tonInstance, walletInfo: walletInfo, decryptedSecret: decryptedSecret, localPassword: serverSalt, toAddress: address, amount: amount, textMessage: textMessage, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: 0, randomId: randomId)
|> deliverOnMainQueue).start(error: { [weak self] error in
guard let strongSelf = self else {
return
@@ -227,9 +226,9 @@ public final class WalletSplashScreen: ViewController {
], actionLayout: .vertical), in: .window(.root))
}
strongSelf.present(controller, in: .window(.root))
- let _ = (getServerWalletSalt(network: strongSelf.context.network)
+ let _ = (strongSelf.context.getServerSalt()
|> deliverOnMainQueue).start(next: { serverSalt in
- let _ = (createWallet(postbox: strongSelf.context.postbox, tonInstance: strongSelf.context.tonInstance, keychain: strongSelf.context.keychain, localPassword: serverSalt)
+ let _ = (createWallet(storage: strongSelf.context.storage, tonInstance: strongSelf.context.tonInstance, keychain: strongSelf.context.keychain, localPassword: serverSalt)
|> deliverOnMainQueue).start(next: { walletInfo, wordList in
guard let strongSelf = self else {
return
@@ -252,7 +251,7 @@ public final class WalletSplashScreen: ViewController {
let context = strongSelf.context
let _ = (strongSelf.context.keychain.decrypt(walletInfo.encryptedSecret)
|> deliverOnMainQueue).start(next: { [weak controller] decryptedSecret in
- let _ = (getServerWalletSalt(network: context.network)
+ let _ = (context.getServerSalt()
|> deliverOnMainQueue).start(next: { [weak controller] serverSalt in
let _ = (walletRestoreWords(tonInstance: context.tonInstance, publicKey: walletInfo.publicKey, decryptedSecret: decryptedSecret, localPassword: serverSalt)
|> deliverOnMainQueue).start(next: { wordList in
@@ -539,7 +538,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = parseMarkdownIntoAttributedString(self.presentationData.strings.Wallet_Intro_Terms, attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center)
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletIntroLoading", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 248, height: 248, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 248, height: 248, mode: .direct)
self.animationSize = CGSize(width: 124.0, height: 124.0)
self.animationNode.visibility = true
}
@@ -551,7 +550,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletCreated", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 250, height: 250, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 250, height: 250, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 125.0, height: 125.0)
self.animationNode.visibility = true
}
@@ -563,7 +562,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletDone", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 260, height: 260, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 130.0, height: 130.0)
self.animationNode.visibility = true
}
@@ -575,7 +574,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletNotAvailable", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 260, height: 260, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 130.0, height: 130.0)
self.animationNode.visibility = true
}
@@ -587,7 +586,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "SendingGrams", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 260, height: 260, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, mode: .direct)
self.animationSize = CGSize(width: 130.0, height: 130.0)
self.animationNode.visibility = true
}
@@ -601,7 +600,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletDone", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 260, height: 260, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 130.0, height: 130.0)
self.animationNode.visibility = true
}
@@ -613,7 +612,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletKeyLock", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 280, height: 280, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 280, height: 280, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 140.0, height: 140.0)
self.animationNode.visibility = true
}
@@ -658,7 +657,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
termsText = NSAttributedString(string: "")
self.iconNode.image = nil
if let path = getAppBundle().path(forResource: "WalletNotAvailable", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 260, height: 260, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 260, height: 260, playbackMode: .once, mode: .direct)
self.animationSize = CGSize(width: 130.0, height: 130.0)
self.animationNode.visibility = true
}
diff --git a/submodules/WalletUI/Sources/WalletStrings.swift b/submodules/WalletUI/Sources/WalletStrings.swift
index 8e476626cd..c8ffa7e9bf 100644
--- a/submodules/WalletUI/Sources/WalletStrings.swift
+++ b/submodules/WalletUI/Sources/WalletStrings.swift
@@ -414,12 +414,12 @@ public final class WalletStrings: Equatable {
public var Wallet_Info_RefreshErrorText: String { return self._s[188]! }
public var Wallet_SecureStorageReset_Title: String { return self._s[189]! }
public var Wallet_Receive_CommentHeader: String { return self._s[190]! }
- public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
+ public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[0 * 6 + Int(form.rawValue)]!, stringValue)
}
- public func Wallet_Updated_MinutesAgo(_ value: Int32) -> String {
+ public func Wallet_Updated_HoursAgo(_ value: Int32) -> String {
let form = getPluralizationForm(self.lc, value)
let stringValue = walletStringsFormattedNumber(value, self.groupingSeparator)
return String(format: self._ps[1 * 6 + Int(form.rawValue)]!, stringValue)
diff --git a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift
index 47fac86e61..6a836f01e1 100644
--- a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift
+++ b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift
@@ -3,12 +3,10 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
-import AnimationUI
import SwiftSignalKit
import OverlayStatusController
+import WalletCore
private func stringForFullDate(timestamp: Int32, strings: WalletStrings, dateTimeFormat: WalletPresentationDateTimeFormat) -> String {
var t: time_t = Int(timestamp)
@@ -153,7 +151,7 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
case let .infoHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .infoAddress(theme, text, address):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], font: .monospace, sectionId: self.section, style: .blocks, longTapAction: address == nil ? nil : {
+ return ItemListMultilineTextItem(theme: theme, text: text, font: .monospace, sectionId: self.section, style: .blocks, longTapAction: address == nil ? nil : {
if let address = address {
arguments.displayContextMenu(WalletTransactionInfoEntryTag.address, address)
}
@@ -169,7 +167,7 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
case let .storageFeeHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .storageFee(theme, text):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
+ return ItemListMultilineTextItem(theme: theme, text: text, sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
case let .storageFeeInfo(theme, text):
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section, linkAction: { action in
switch action {
@@ -180,7 +178,7 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
case let .otherFeeHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .otherFee(theme, text):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
+ return ItemListMultilineTextItem(theme: theme, text: text, sectionId: self.section, style: .blocks, longTapAction: nil, tag: nil)
case let .otherFeeInfo(theme, text):
return ItemListTextItem(theme: theme, text: .markdown(text), sectionId: self.section, linkAction: { action in
switch action {
@@ -191,7 +189,7 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry {
case let .commentHeader(theme, text):
return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section)
case let .comment(theme, text):
- return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks, longTapAction: {
+ return ItemListMultilineTextItem(theme: theme, text: text, sectionId: self.section, style: .blocks, longTapAction: {
arguments.displayContextMenu(WalletTransactionInfoEntryTag.comment, text)
}, tag: WalletTransactionInfoEntryTag.comment)
}
@@ -347,7 +345,9 @@ func walletTransactionInfoController(context: WalletContext, walletInfo: WalletI
let address = extractAddress(walletTransaction)
if case let .list(addresses) = address, let address = addresses.first {
dismissImpl?()
- pushImpl?(walletSendScreen(context: context, randomId: arc4random64(), walletInfo: walletInfo, address: address))
+ var randomId: Int64 = 0
+ arc4random_buf(&randomId, 8)
+ pushImpl?(walletSendScreen(context: context, randomId: randomId, walletInfo: walletInfo, address: address))
}
}, displayContextMenu: { tag, text in
displayContextMenuImpl?(tag, text)
diff --git a/submodules/WalletUI/Sources/WalletUtils.swift b/submodules/WalletUI/Sources/WalletUtils.swift
index f9ceebd325..2be5d60849 100644
--- a/submodules/WalletUI/Sources/WalletUtils.swift
+++ b/submodules/WalletUI/Sources/WalletUtils.swift
@@ -2,7 +2,7 @@ import Foundation
import UrlEscaping
let walletAddressLength: Int = 48
-let walletTextLimit: Int = 1024
+let walletTextLimit: Int = 512
func formatAddress(_ address: String) -> String {
var address = address
diff --git a/submodules/WalletUI/Sources/WalletWordCheckScreen.swift b/submodules/WalletUI/Sources/WalletWordCheckScreen.swift
index 4d28ad4794..282f0e2fcb 100644
--- a/submodules/WalletUI/Sources/WalletWordCheckScreen.swift
+++ b/submodules/WalletUI/Sources/WalletWordCheckScreen.swift
@@ -3,14 +3,12 @@ import UIKit
import AppBundle
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
-import UndoUI
import AlertUI
import SwiftSignalKit
-import TextFormat
-import AnimationUI
+import AnimatedStickerNode
+import WalletCore
+import Markdown
private let possibleWordList: [String] = [
"abandon",
@@ -2138,7 +2136,7 @@ public final class WalletWordCheckScreen: ViewController {
}
return true
}
- let _ = confirmWalletExported(postbox: strongSelf.context.postbox, walletInfo: walletInfo).start()
+ let _ = confirmWalletExported(storage: strongSelf.context.storage, publicKey: walletInfo.publicKey).start()
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
@@ -2186,9 +2184,9 @@ public final class WalletWordCheckScreen: ViewController {
], actionLayout: .vertical), in: .window(.root))
}
- let _ = (getServerWalletSalt(network: strongSelf.context.network)
+ let _ = (strongSelf.context.getServerSalt()
|> deliverOnMainQueue).start(next: { serverSalt in
- let _ = (importWallet(postbox: strongSelf.context.postbox, tonInstance: strongSelf.context.tonInstance, keychain: strongSelf.context.keychain, wordList: enteredWords, localPassword: serverSalt)
+ let _ = (importWallet(storage: strongSelf.context.storage, tonInstance: strongSelf.context.tonInstance, keychain: strongSelf.context.keychain, wordList: enteredWords, localPassword: serverSalt)
|> deliverOnMainQueue).start(next: { walletInfo in
guard let strongSelf = self else {
return
@@ -2624,7 +2622,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro
buttonText = self.presentationData.strings.Wallet_WordCheck_Continue
secondaryActionText = ""
if let path = getAppBundle().path(forResource: "WalletWordCheck", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 238, height: 238, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 238, height: 238, playbackMode: .once, mode: .direct)
self.animationNode.visibility = true
}
case .import:
diff --git a/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift b/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift
index fdac4921bf..2c4d4b68be 100644
--- a/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift
+++ b/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift
@@ -4,12 +4,10 @@ import AppBundle
import SwiftSignalKit
import AsyncDisplayKit
import Display
-import Postbox
-import TelegramCore
import SolidRoundedButtonNode
-import UndoUI
import AlertUI
-import AnimationUI
+import AnimatedStickerNode
+import WalletCore
public enum WalletWordDisplayScreenMode {
case check
@@ -79,7 +77,7 @@ public final class WalletWordDisplayScreen: ViewController {
let deltaTime = Date().timeIntervalSince1970 - strongSelf.startTime
let minimalTimeout: Double
#if DEBUG
- minimalTimeout = 1.0
+ minimalTimeout = 60.0
#else
minimalTimeout = 60.0
#endif
@@ -88,10 +86,7 @@ public final class WalletWordDisplayScreen: ViewController {
guard let strongSelf = self else {
return
}
- if let path = getAppBundle().path(forResource: "thumbsup", ofType: "tgs") {
- let controller = UndoOverlayController(presentationData: strongSelf.presentationData, content: UndoOverlayContent.emoji(path: path, text: strongSelf.presentationData.strings.Wallet_Words_NotDoneResponse), elevatedLayout: false, animateInAsReplacement: false, action: { _ in })
- strongSelf.present(controller, in: .current)
- }
+ (strongSelf.displayNode as! WalletWordDisplayScreenNode).displayToast()
})]), in: .window(.root))
} else {
var wordIndices: [Int] = []
@@ -131,6 +126,9 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
private let wordNodes: [(ImmediateTextNode, ImmediateTextNode, ImmediateTextNode)]
private let buttonNode: SolidRoundedButtonNode
+ private var toastNode: ToastNode?
+
+ private var validLayout: (ContainerViewLayout, CGFloat)?
private var navigationHeight: CGFloat?
init(presentationData: WalletPresentationData, wordList: [String], action: @escaping () -> Void) {
@@ -148,7 +146,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
self.animationNode = AnimatedStickerNode()
if let path = getAppBundle().path(forResource: "WalletWordList", ofType: "tgs") {
- self.animationNode.setup(resource: .localFile(path), width: 264, height: 264, playbackMode: .once, mode: .direct)
+ self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 264, height: 264, playbackMode: .once, mode: .direct)
self.animationNode.visibility = true
}
@@ -283,6 +281,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
}
func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
+ self.validLayout = (layout, navigationHeight)
self.navigationHeight = navigationHeight
let sideInset: CGFloat = 32.0
@@ -385,4 +384,28 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc
self.updateTitle()
}
+
+ func displayToast() {
+ if self.toastNode != nil {
+ return
+ }
+
+ if let path = getAppBundle().path(forResource: "WalletApologiesAccepted", ofType: "tgs") {
+ let toastNode = ToastNode(theme: self.presentationData.theme, animationPath: path, text: self.presentationData.strings.Wallet_Words_NotDoneResponse)
+ self.toastNode = toastNode
+ if let (layout, navigationHeight) = self.validLayout {
+ toastNode.update(layout: layout, transition: .immediate)
+ }
+ self.addSubnode(toastNode)
+ toastNode.show(removed: { [weak self, weak toastNode] in
+ guard let strongSelf = self, let toastNode = toastNode else {
+ return
+ }
+ toastNode.removeFromSupernode()
+ if toastNode === strongSelf.toastNode {
+ strongSelf.toastNode = nil
+ }
+ })
+ }
+ }
}
diff --git a/submodules/WalletUrl/BUCK b/submodules/WalletUrl/BUCK
new file mode 100644
index 0000000000..f7042f93d4
--- /dev/null
+++ b/submodules/WalletUrl/BUCK
@@ -0,0 +1,14 @@
+load("//Config:buck_rule_macros.bzl", "static_library")
+
+static_library(
+ name = "WalletUrl",
+ srcs = glob([
+ "Sources/**/*.swift",
+ ]),
+ deps = [
+
+ ],
+ frameworks = [
+ "$SDKROOT/System/Library/Frameworks/Foundation.framework",
+ ],
+)
diff --git a/submodules/WalletUrl/Sources/WalletUrl.swift b/submodules/WalletUrl/Sources/WalletUrl.swift
new file mode 100644
index 0000000000..e55cdd0d18
--- /dev/null
+++ b/submodules/WalletUrl/Sources/WalletUrl.swift
@@ -0,0 +1,40 @@
+import Foundation
+
+public struct ParsedWalletUrl {
+ public let address: String
+ public let amount: Int64?
+ public let comment: String?
+}
+
+private let invalidWalletAddressCharacters = CharacterSet(charactersIn: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=").inverted
+private func isValidWalletAddress(_ address: String) -> Bool {
+ if address.count != 48 || address.rangeOfCharacter(from: invalidWalletAddressCharacters) != nil {
+ return false
+ }
+ return true
+}
+
+public func parseWalletUrl(_ url: URL) -> ParsedWalletUrl? {
+ guard url.scheme == "ton" && url.host == "transfer" else {
+ return nil
+ }
+ var address: String?
+ let path = url.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
+ if isValidWalletAddress(path) {
+ address = path
+ }
+ var amount: Int64?
+ var comment: String?
+ if let query = url.query, let components = URLComponents(string: "/?" + query), let queryItems = components.queryItems {
+ for queryItem in queryItems {
+ if let value = queryItem.value {
+ if queryItem.name == "amount", !value.isEmpty, let amountValue = Int64(value) {
+ amount = amountValue
+ } else if queryItem.name == "text", !value.isEmpty {
+ comment = value
+ }
+ }
+ }
+ }
+ return address.flatMap { ParsedWalletUrl(address: $0, amount: amount, comment: comment) }
+}
diff --git a/submodules/WebSearchUI/BUCK b/submodules/WebSearchUI/BUCK
index f9bd159378..3ff9abbe7e 100644
--- a/submodules/WebSearchUI/BUCK
+++ b/submodules/WebSearchUI/BUCK
@@ -26,6 +26,7 @@ static_library(
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
"//submodules/SegmentedControlNode:SegmentedControlNode",
"//submodules/AppBundle:AppBundle",
+ "//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
diff --git a/tools/GenerateLocalization.sh b/tools/GenerateLocalization.sh
index 326e5ce5b8..203a1ed412 100644
--- a/tools/GenerateLocalization.sh
+++ b/tools/GenerateLocalization.sh
@@ -5,3 +5,13 @@ swift -swift-version 4 tools/GenerateLocalization.swift Telegram-iOS/en.lproj/Lo
mkdir -p submodules/WalletUI/Resources
swift -swift-version 4 tools/GenerateLocalization.swift Telegram-iOS/en.lproj/Localizable.strings submodules/WalletUI/Sources/WalletStrings.swift submodules/WalletUI/Resources/WalletStrings.mapping "Wallet."
+wallet_strings_path="Wallet/Strings"
+strings_name="Localizable.strings"
+rm -rf "$wallet_strings_path"
+
+for f in $(basename $(find "Telegram-iOS" -name "*.lproj")); do
+ mkdir -p "$wallet_strings_path/$f"
+ if [ -f "Telegram-iOS/$f/$strings_name" ]; then
+ cat "Telegram-iOS/$f/$strings_name" | grep -E '^"Wallet\..*?$' > "$wallet_strings_path/$f/$strings_name"
+ fi
+done