diff --git a/CLAUDE.md b/CLAUDE.md index 801a7996bd..adfca9c415 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ python3 build-system/Make/Make.py --overrideXcodeVersion \ --cacheDir ~/telegram-bazel-cache \ build \ --configurationPath build-system/appstore-configuration.json \ - --gitCodesigningRepository git@gitlab.com:peter-iakovlev/fastlanematch.git \ + --gitCodesigningRepository \ --gitCodesigningType development --gitCodesigningUseCurrent --buildNumber=1 --configuration=debug_sim_arm64 ``` diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000000..171e110848 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,10 @@ +# Maintainers + +WinterGram is developed and maintained under the [reekeer](https://github.com/reekeer) organization. + +| Maintainer | GitHub | +| :--- | :--- | +| IMDelewer | [@IMDelewer](https://github.com/IMDelewer) | +| salenyo | [@salenyo](https://github.com/salenyo) | + +For questions, contributions, or issues, open a pull request or issue on the [WinterGram repository](https://github.com/reekeer/WinterGram). diff --git a/README.md b/README.md index 79f325aa13..2ba6771007 100644 --- a/README.md +++ b/README.md @@ -1,116 +1,122 @@ -# Telegram iOS Source Code Compilation Guide +

WinterGram

-We welcome all developers to use our API and source code to create applications on our platform. -There are several things we require from **all developers** for the moment. +

WinterGram (Wnt) is a feature-rich, privacy-focused Telegram client for iPhone β€” a native iOS port of the AyuGram experience, built on top of Telegram-iOS.

-# Creating your Telegram Application +

+ License + Platform + Swift + Bazel + Stars + Last Commit +

-1. [**Obtain your own api_id**](https://core.telegram.org/api/obtaining_api_id) for your application. -2. Please **do not** use the name Telegram for your app β€” or make sure your users understand that it is unofficial. -3. Kindly **do not** use our standard logo (white paper plane in a blue circle) as your app's logo. -3. Please study our [**security guidelines**](https://core.telegram.org/mtproto/security_guidelines) and take good care of your users' data and privacy. -4. Please remember to publish **your** code too in order to comply with the licences. +--- -# Quick Compilation Guide +**WinterGram** brings the most-loved AyuGram features to iOS with a clean, Material-inspired interface, a configurable **Liquid Glass** appearance, and a single dedicated settings tab where everything lives. It speaks both the standard `tg://` deep links and its own `wnt://` scheme. -## Get the Code +--- + +## ✨ Features + +### πŸ‘» Privacy & Ghost Mode +- **Ghost Mode**: don't send read receipts, typing/upload status, or online presence β€” toggle it all at once. +- **Send without sound**: never / only in Ghost Mode / always. +- **Story ghost**: view stories without marking them seen, with an optional confirmation prompt. +- **Mark read after action**, **go offline after going online**, and per-toggle locks. + +### πŸ—‚ History & Recovery +- **Save deleted messages**: keep messages locally even after the other side deletes them. +- **Edit history**: store every revision of a message and browse it. +- **Semi-transparent deleted markers** and a customizable deleted / edited mark. + +### 🧊 Hidden Archive ("AАrchive") +- Stash chats into a **separate, settings-only archive** β€” no notifications, no badge. +- Optional **auto-mark-read** for everything sent to the stash. + +### πŸ›‘οΈ Anti-Features +- **Disable ads** (sponsored messages). +- **Local Telegram Premium** β€” unlock Premium-gated UI locally. +- **Shadow ban** β€” silently hide a user's messages from your view. +- **Hide / disable stories**, **hide Premium statuses**, **disable open-link warning**. + +### πŸ’¬ Chat Conveniences +- **Sticker / GIF / voice send confirmations**. +- **Message seconds** in timestamps and **peer ID** display (Telegram or Bot API form). +- **Message translation** with a selectable provider (Telegram / Google / Yandex / system). +- **WebView platform spoofing** (auto / iOS / Android / macOS / desktop) and taller WebViews. + +### 🎨 Appearance & Customization +- **Liquid Glass** β€” frosted, translucent surfaces across the chat list, navigation, and tab bar, with **on/off toggle**, adjustable **transparency**, **blur radius**, **tint**, and per-surface application. +- **Material Design** switches and controls. +- **Avatar corner radius** (round β†’ squircle β†’ square) and **message bubble radius**, with optional single-corner mode. +- **Custom fonts** (UI + monospace). +- **App icons**, including the bundled WinterGram dark icon, plus **AyuGram / exteraGram icon-pack compatibility**. +- **Custom emoji** support, with an option to show only your added emoji & stickers. + +--- + +## πŸ”— Deep Links + +WinterGram registers and resolves two URL schemes β€” anything that works with `tg://` works with `wnt://`: ``` -git clone --recursive -j8 https://github.com/TelegramMessenger/Telegram-iOS.git +tg://resolve?domain=durov +wnt://resolve?domain=durov ``` -## Setup Xcode +`wnt://` links are normalized to the standard resolver at the entry point, so they route through exactly the same handling as native Telegram links. -Install Xcode (directly from https://developer.apple.com/download/applications or using the App Store). +--- -## Adjust Configuration +## πŸš€ Build -1. Generate a random identifier: -``` -openssl rand -hex 8 -``` -2. Create a new Xcode project. Use `Telegram` as the Product Name. Use `org.{identifier from step 1}` as the Organization Identifier. -3. Open `Keychain Access` and navigate to `Certificates`. Locate `Apple Development: your@email.address (XXXXXXXXXX)` and double tap the certificate. Under `Details`, locate `Organizational Unit`. This is the Team ID. -4. Edit `build-system/template_minimal_development_configuration.json`. Use data from the previous steps. +WinterGram builds with the standard Telegram-iOS toolchain (Bazel via the `Make.py` wrapper) on **macOS with Xcode**. -## Generate an Xcode project - -``` -python3 build-system/Make/Make.py \ - --cacheDir="$HOME/telegram-bazel-cache" \ - generateProject \ - --configurationPath=build-system/template_minimal_development_configuration.json \ - --xcodeManagedCodesigning +```sh +python3 build-system/Make/Make.py --overrideXcodeVersion \ + --cacheDir ~/telegram-bazel-cache \ + build \ + --configurationPath build-system/appstore-configuration.json \ + --gitCodesigningRepository \ + --gitCodesigningType development --gitCodesigningUseCurrent \ + --buildNumber=1 --configuration=debug_sim_arm64 ``` -# Advanced Compilation Guide +See [`docs/wintergram-features.md`](docs/wintergram-features.md) for the feature β†’ implementation map and the project's architecture notes. -## Xcode +--- -1. Copy and edit `build-system/appstore-configuration.json`. -2. Copy `build-system/fake-codesigning`. Create and download provisioning profiles, using the `profiles` folder as a reference for the entitlements. -3. Generate an Xcode project: -``` -python3 build-system/Make/Make.py \ - --cacheDir="$HOME/telegram-bazel-cache" \ - generateProject \ - --configurationPath=configuration_from_step_1.json \ - --codesigningInformationPath=directory_from_step_2 -``` +## βš™οΈ Configuration -## IPA +All WinterGram options live in a single settings store (`WinterGramSettings`), persisted with the app's shared-data system and exposed through reactive signals. There is one dedicated **WinterGram** tab in Settings β€” no scattered toggles. -1. Repeat the steps from the previous section. Use distribution provisioning profiles. -2. Run: -``` -python3 build-system/Make/Make.py \ - --cacheDir="$HOME/telegram-bazel-cache" \ - build \ - --configurationPath=...see previous section... \ - --codesigningInformationPath=...see previous section... \ - --buildNumber=100001 \ - --configuration=release_arm64 -``` +--- -# FAQ - -## Xcode is stuck at "build-request.json not updated yet" - -Occasionally, you might observe the following message in your build log: -``` -"/Users/xxx/Library/Developer/Xcode/DerivedData/Telegram-xxx/Build/Intermediates.noindex/XCBuildData/xxx.xcbuilddata/build-request.json" not updated yet, waiting... -``` - -Should this occur, simply cancel the ongoing build and initiate a new one. - -## Telegram_xcodeproj: no such package - -Following a system restart, the auto-generated Xcode project might encounter a build failure accompanied by this error: -``` -ERROR: Skipping '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj:Telegram_xcodeproj': no such package '@rules_xcodeproj_generated//generator/Telegram/Telegram_xcodeproj': BUILD file not found in directory 'generator/Telegram/Telegram_xcodeproj' of external repository @rules_xcodeproj_generated. Add a BUILD file to a directory to mark it as a package. -``` - -If you encounter this issue, re-run the project generation steps in the README. - - -# Tips - -## Codesigning is not required for simulator-only builds - -Add `--disableProvisioningProfiles`: -``` -python3 build-system/Make/Make.py \ - --cacheDir="$HOME/telegram-bazel-cache" \ - generateProject \ - --configurationPath=path-to-configuration.json \ - --codesigningInformationPath=path-to-provisioning-data \ - --disableProvisioningProfiles -``` - -## Versions - -Each release is built using a specific Xcode version (see `versions.json`). The helper script checks the versions of the installed software and reports an error if they don't match the ones specified in `versions.json`. It is possible to bypass these checks: +## πŸ—‚ Structure ``` -python3 build-system/Make/Make.py --overrideXcodeVersion build ... # Don't check the version of Xcode +WinterGram/ +β”œβ”€β”€ Telegram/ ← App entry points and extensions +β”œβ”€β”€ submodules/ ← Feature libraries (Swift / Obj-C) +β”‚ └── TelegramUIPreferences/ +β”‚ └── Sources/WinterGramSettings.swift ← all WinterGram options +β”œβ”€β”€ branding/ ← WinterGram icons and brand assets +β”œβ”€β”€ docs/ ← Architecture and feature documentation +β”œβ”€β”€ build-system/ ← Bazel build wrapper (Make.py) +└── README.md ``` + +--- + +## 🀝 Contributing + +Contributions are welcome. WinterGram is maintained by [**IMDelewer**](https://github.com/IMDelewer) and [**salenyo**](https://github.com/salenyo) under the [reekeer](https://github.com/reekeer) organization. See [`MAINTAINERS.md`](MAINTAINERS.md). + +--- + +

+ Built on Telegram-iOS Β· inspired by AyuGram +

+ +

GPLv2 Β© reekeer

diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Contents.json b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Contents.json new file mode 100644 index 0000000000..172ed83a1f --- /dev/null +++ b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Contents.json @@ -0,0 +1,116 @@ +{ + "images" : [ + { + "filename" : "Icon-40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-58.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-87.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-80.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-120.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-120.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-180.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "Icon-20.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "Icon-40.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-29.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-58.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-40.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-80.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-76.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "Icon-152.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "Icon-167.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "Icon-1024.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "reekeer", + "version" : 1 + } +} diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-1024.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-1024.png new file mode 100644 index 0000000000..7a301e6332 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-1024.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-120.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-120.png new file mode 100644 index 0000000000..a65b5897a8 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-120.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-152.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-152.png new file mode 100644 index 0000000000..dd7a6f1dd6 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-152.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-167.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-167.png new file mode 100644 index 0000000000..02d5cf0e9d Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-167.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-180.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-180.png new file mode 100644 index 0000000000..926d014f30 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-180.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-20.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-20.png new file mode 100644 index 0000000000..6aee2e35c4 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-20.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-29.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-29.png new file mode 100644 index 0000000000..1fea6d62a4 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-29.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-40.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-40.png new file mode 100644 index 0000000000..dcd3c0784f Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-40.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-58.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-58.png new file mode 100644 index 0000000000..cd11b73ca2 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-58.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-60.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-60.png new file mode 100644 index 0000000000..294da6cf0a Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-60.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-76.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-76.png new file mode 100644 index 0000000000..200a039341 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-76.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-80.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-80.png new file mode 100644 index 0000000000..d1c6ea5932 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-80.png differ diff --git a/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-87.png b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-87.png new file mode 100644 index 0000000000..ede13b9a80 Binary files /dev/null and b/Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset/Icon-87.png differ diff --git a/Telegram/Telegram-iOS/Info.plist b/Telegram/Telegram-iOS/Info.plist index a34aee90c5..389f866c2b 100644 --- a/Telegram/Telegram-iOS/Info.plist +++ b/Telegram/Telegram-iOS/Info.plist @@ -212,6 +212,7 @@ CFBundleURLSchemes tg + wnt $(APP_SPECIFIC_URL_SCHEME) diff --git a/Telegram/Telegram-iOS/InfoBazel.plist b/Telegram/Telegram-iOS/InfoBazel.plist index ae1b92de45..e9b91c4c41 100644 --- a/Telegram/Telegram-iOS/InfoBazel.plist +++ b/Telegram/Telegram-iOS/InfoBazel.plist @@ -46,6 +46,7 @@ CFBundleURLSchemes tg + wnt $(APP_SPECIFIC_URL_SCHEME) diff --git a/branding/wintergram-icon.png b/branding/wintergram-icon.png new file mode 100644 index 0000000000..0cf3146a0a Binary files /dev/null and b/branding/wintergram-icon.png differ diff --git a/branding/wintergram-icon.svg b/branding/wintergram-icon.svg new file mode 100644 index 0000000000..ef7e7dae2a --- /dev/null +++ b/branding/wintergram-icon.svg @@ -0,0 +1,54931 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/wintergram-features.md b/docs/wintergram-features.md new file mode 100644 index 0000000000..dcbe4aff27 --- /dev/null +++ b/docs/wintergram-features.md @@ -0,0 +1,96 @@ +# WinterGram β€” feature β†’ implementation map + +This document maps every WinterGram option to where it is (or needs to be) wired into the +Telegram-iOS codebase. The settings themselves live in a single store: + +- `submodules/TelegramUIPreferences/Sources/WinterGramSettings.swift` β€” the `WinterGramSettings` + `Codable` struct, its sub-structs (`WinterGramLiquidGlass`) and enums, plus + `updateWinterGramSettingsInteractively(...)` and `winterGramSettings(accountManager:)`. +- `submodules/TelegramUIPreferences/Sources/PostboxKeys.swift` β€” the shared-data key + `ApplicationSpecificSharedDataKeys.winterGramSettings` (value `23`). + +Read the store anywhere that already has an `AccountContext` / `AccountManager` via +`winterGramSettings(accountManager:)`, and write it via `updateWinterGramSettingsInteractively`. + +## Status legend + +- **Store** β€” the setting exists and persists (done in this repo). +- **Hook** β€” the place in the app where behavior must read the setting. + +## Privacy & Ghost Mode + +| Setting | Hook | +| :-- | :-- | +| `ghostModeEnabled` | Master switch read by the read-receipt / online-status / typing senders below. | +| `sendReadReceipts` | `TelegramCore` history read β€” gate `_internal_applyMaxReadIndex` / outgoing `messages.readHistory` calls. | +| `sendReadStories` | Story view reporting β€” gate `markStoryAsSeen` network calls. | +| `sendOnlineStatus` | Online presence β€” gate `account.updatePresence` / `updateStatus`. | +| `sendUploadProgress` | Typing/upload activity β€” gate `ChatActivity` / `setTyping` reporting. | +| `sendOfflineAfterOnline` | Emit a one-shot offline presence packet after the app goes online. | +| `markReadAfterAction` | After replying/reacting, locally mark the chat read without sending receipts. | +| `useScheduledMessages` | "ΠžΡ‚Π»ΠΎΠΆΠΊΠ°" β€” when ghosting, send via the scheduled-messages path. | +| `sendWithoutSound` | Outgoing message flags β€” set `silent` per `shouldSendWithoutSound`. | +| `suggestGhostBeforeStory` | Story viewer β€” present the ghost confirmation before opening. | + +## History & Recovery + +| Setting | Hook | +| :-- | :-- | +| `saveDeletedMessages` | Hook the deletion path in `Postbox` history removal; mirror messages into a local store before they are purged. | +| `saveMessagesHistory` | On `EditMessage` updates, append the previous version to a local edit-history store. | +| `semiTransparentDeletedMessages` | `ChatMessageItemView` β€” render saved-deleted bubbles at reduced alpha. | +| `deletedMark` / `editedMark` | Message footer rendering in the bubble content nodes. | + +## Hidden Archive ("AАrchive") + +| Setting | Hook | +| :-- | :-- | +| `stashedPeerIds` | Filter the chat list to hide these peers from the main list; show them only in the dedicated WinterGram archive screen. | +| `stashMuteNotifications` | Notification service extension β€” suppress notifications for stashed peers. | +| `stashAutoMarkRead` | On receiving from a stashed peer, locally mark read (respecting Ghost Mode). | + +## Anti-Features + +| Setting | Hook | +| :-- | :-- | +| `disableAds` | Sponsored-messages fetch in `TelegramCore` (`getAdMessages`) β€” return empty when disabled. | +| `localPremium` | `isPremium` resolution in the UI layer β€” treat as Premium locally for gated UI. | +| `shadowBanIds` | Chat history filtering β€” drop incoming messages from these peers from the rendered list. | +| `disableStories` | Story list assembly β€” hide the stories strip. | +| `hidePremiumStatuses` | Peer title rendering β€” drop Premium/emoji-status badges. | +| `disableOpenLinkWarning` | URL open path β€” skip the "open this link?" confirmation. | + +## Chat Conveniences + +| Setting | Hook | +| :-- | :-- | +| `stickerConfirmation` / `gifConfirmation` / `voiceConfirmation` | Send paths in the chat input panel β€” present a confirm alert before sending. | +| `showMessageSeconds` | Timestamp formatting in the bubble footer. | +| `showPeerId` | Peer info / chat title β€” append the ID in Telegram or Bot API form. | +| `translateMessages` / `translationProvider` | Message context menu translate action + provider selection. | +| `webviewSpoofPlatform` / `increaseWebviewHeight` | WebApp controller β€” set the spoofed `tg_platform` / viewport height. | + +## Appearance & Customization + +| Setting | Hook | +| :-- | :-- | +| `liquidGlass.*` | `Display` blur/material layers behind the chat list, nav bar, and tab bar; read `enabled`, `transparency`, `blurRadius`, `tintColor`, per-surface flags. | +| `materialDesign` | Switch/control styling in `ItemListUI` components. | +| `avatarCornerRadius` / `singleCornerRadius` | Avatar node corner rounding in `AvatarNode`. | +| `messageBubbleRadius` / `removeMessageTail` | Bubble background drawing in the chat message backgrounds. | +| `customFont` / `monoFont` | `PresentationData` font resolution. | +| `appIcon` / `iconPack` | Alternate-icon switching via `UIApplication.setAlternateIconName`; see `Telegram/Telegram-iOS/DefaultAppIcon.xcassets/WinterGramDarkIcon.appiconset`. | +| `showOnlyAddedEmojisAndStickers` | Emoji/sticker panel data sources β€” filter to installed packs. | + +## Deep links β€” `wnt://` + +Registered in `Telegram/Telegram-iOS/Info.plist` and `InfoBazel.plist` (alongside `tg`). +Normalized to `tg://` at the app entry by `normalizeWinterGramUrlScheme(_:)` in +`submodules/TelegramUI/Sources/AppDelegate.swift`, so every `tg://` route also accepts `wnt://`. + +## Settings UI + +A dedicated **WinterGram** entry should be added to the settings list +(`submodules/SettingsUI` / the PeerInfo settings screen) that opens an `ItemListController` +backed by `winterGramSettings(accountManager:)` and writing through +`updateWinterGramSettingsInteractively`. Group the rows by the sections above. diff --git a/submodules/AccountUtils/Sources/AccountUtils.swift b/submodules/AccountUtils/Sources/AccountUtils.swift index 44d09f560f..68785fc75e 100644 --- a/submodules/AccountUtils/Sources/AccountUtils.swift +++ b/submodules/AccountUtils/Sources/AccountUtils.swift @@ -4,8 +4,8 @@ import TelegramCore import TelegramUIPreferences import AccountContext -public let maximumNumberOfAccounts = 3 -public let maximumPremiumNumberOfAccounts = 4 +public let maximumNumberOfAccounts = 100 +public let maximumPremiumNumberOfAccounts = 100 public func activeAccountsAndPeers(context: AccountContext, includePrimary: Bool = false) -> Signal<((AccountContext, EnginePeer)?, [(AccountContext, EnginePeer, Int32)]), NoError> { let sharedContext = context.sharedContext diff --git a/submodules/SettingsUI/Sources/WinterGramSettingsController.swift b/submodules/SettingsUI/Sources/WinterGramSettingsController.swift new file mode 100644 index 0000000000..97a2721faf --- /dev/null +++ b/submodules/SettingsUI/Sources/WinterGramSettingsController.swift @@ -0,0 +1,607 @@ +import Foundation +import UIKit +import Display +import SwiftSignalKit +import TelegramCore +import TelegramPresentationData +import TelegramUIPreferences +import ItemListUI +import PresentationDataUtils +import AccountContext + +private final class WinterGramSettingsArguments { + let updateSettings: (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void + let presentSendWithoutSound: () -> Void + let presentPeerId: () -> Void + let presentTranslationProvider: () -> Void + let presentWebviewPlatform: () -> Void + let presentIconPack: () -> Void + + init( + updateSettings: @escaping (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void, + presentSendWithoutSound: @escaping () -> Void, + presentPeerId: @escaping () -> Void, + presentTranslationProvider: @escaping () -> Void, + presentWebviewPlatform: @escaping () -> Void, + presentIconPack: @escaping () -> Void + ) { + self.updateSettings = updateSettings + self.presentSendWithoutSound = presentSendWithoutSound + self.presentPeerId = presentPeerId + self.presentTranslationProvider = presentTranslationProvider + self.presentWebviewPlatform = presentWebviewPlatform + self.presentIconPack = presentIconPack + } +} + +private enum WinterGramSettingsSection: Int32 { + case ghost + case history + case stash + case antiFeatures + case confirmations + case chat + case appearance + case liquidGlass +} + +private enum WinterGramSettingsEntry: ItemListNodeEntry { + case ghostHeader + case ghostEnabled(Bool) + case ghostReadReceipts(Bool) + case ghostReadStories(Bool) + case ghostOnlineStatus(Bool) + case ghostUploadProgress(Bool) + case ghostOfflineAfterOnline(Bool) + case ghostMarkReadAfterAction(Bool) + case ghostUseScheduled(Bool) + case ghostSendWithoutSound(String) + case ghostSuggestBeforeStory(Bool) + case ghostFooter + + case historyHeader + case historySaveDeleted(Bool) + case historySaveEdits(Bool) + case historySemiTransparent(Bool) + case historyFooter + + case stashHeader + case stashMute(Bool) + case stashAutoRead(Bool) + case stashFooter + + case antiHeader + case antiDisableAds(Bool) + case antiLocalPremium(Bool) + case antiDisableStories(Bool) + case antiHidePremiumStatuses(Bool) + case antiDisableLinkWarning(Bool) + case antiFooter + + case confirmHeader + case confirmStickers(Bool) + case confirmGif(Bool) + case confirmVoice(Bool) + + case chatHeader + case chatShowSeconds(Bool) + case chatShowPeerId(String) + case chatTranslate(Bool) + case chatTranslateProvider(String) + case chatWebviewPlatform(String) + case chatWebviewHeight(Bool) + case chatOnlyAddedEmoji(Bool) + + case appearanceHeader + case appearanceMaterial(Bool) + case appearanceSingleCorner(Bool) + case appearanceIconPack(String) + + case glassHeader + case glassEnabled(Bool) + case glassVibrancy(Bool) + case glassChatList(Bool) + case glassNavBars(Bool) + case glassTabBar(Bool) + case glassBubbles(Bool) + case glassFooter + + var section: ItemListSectionId { + switch self { + case .ghostHeader, .ghostEnabled, .ghostReadReceipts, .ghostReadStories, .ghostOnlineStatus, .ghostUploadProgress, .ghostOfflineAfterOnline, .ghostMarkReadAfterAction, .ghostUseScheduled, .ghostSendWithoutSound, .ghostSuggestBeforeStory, .ghostFooter: + return WinterGramSettingsSection.ghost.rawValue + case .historyHeader, .historySaveDeleted, .historySaveEdits, .historySemiTransparent, .historyFooter: + return WinterGramSettingsSection.history.rawValue + case .stashHeader, .stashMute, .stashAutoRead, .stashFooter: + return WinterGramSettingsSection.stash.rawValue + case .antiHeader, .antiDisableAds, .antiLocalPremium, .antiDisableStories, .antiHidePremiumStatuses, .antiDisableLinkWarning, .antiFooter: + return WinterGramSettingsSection.antiFeatures.rawValue + case .confirmHeader, .confirmStickers, .confirmGif, .confirmVoice: + return WinterGramSettingsSection.confirmations.rawValue + case .chatHeader, .chatShowSeconds, .chatShowPeerId, .chatTranslate, .chatTranslateProvider, .chatWebviewPlatform, .chatWebviewHeight, .chatOnlyAddedEmoji: + return WinterGramSettingsSection.chat.rawValue + case .appearanceHeader, .appearanceMaterial, .appearanceSingleCorner, .appearanceIconPack: + return WinterGramSettingsSection.appearance.rawValue + case .glassHeader, .glassEnabled, .glassVibrancy, .glassChatList, .glassNavBars, .glassTabBar, .glassBubbles, .glassFooter: + return WinterGramSettingsSection.liquidGlass.rawValue + } + } + + var stableId: Int32 { + switch self { + case .ghostHeader: return 0 + case .ghostEnabled: return 1 + case .ghostReadReceipts: return 2 + case .ghostReadStories: return 3 + case .ghostOnlineStatus: return 4 + case .ghostUploadProgress: return 5 + case .ghostOfflineAfterOnline: return 6 + case .ghostMarkReadAfterAction: return 7 + case .ghostUseScheduled: return 8 + case .ghostSendWithoutSound: return 9 + case .ghostSuggestBeforeStory: return 10 + case .ghostFooter: return 11 + case .historyHeader: return 12 + case .historySaveDeleted: return 13 + case .historySaveEdits: return 14 + case .historySemiTransparent: return 15 + case .historyFooter: return 16 + case .stashHeader: return 17 + case .stashMute: return 18 + case .stashAutoRead: return 19 + case .stashFooter: return 20 + case .antiHeader: return 21 + case .antiDisableAds: return 22 + case .antiLocalPremium: return 23 + case .antiDisableStories: return 24 + case .antiHidePremiumStatuses: return 25 + case .antiDisableLinkWarning: return 26 + case .antiFooter: return 27 + case .confirmHeader: return 28 + case .confirmStickers: return 29 + case .confirmGif: return 30 + case .confirmVoice: return 31 + case .chatHeader: return 32 + case .chatShowSeconds: return 33 + case .chatShowPeerId: return 34 + case .chatTranslate: return 35 + case .chatTranslateProvider: return 36 + case .chatWebviewPlatform: return 37 + case .chatWebviewHeight: return 38 + case .chatOnlyAddedEmoji: return 39 + case .appearanceHeader: return 40 + case .appearanceMaterial: return 41 + case .appearanceSingleCorner: return 42 + case .appearanceIconPack: return 43 + case .glassHeader: return 44 + case .glassEnabled: return 45 + case .glassVibrancy: return 46 + case .glassChatList: return 47 + case .glassNavBars: return 48 + case .glassTabBar: return 49 + case .glassBubbles: return 50 + case .glassFooter: return 51 + } + } + + static func <(lhs: WinterGramSettingsEntry, rhs: WinterGramSettingsEntry) -> Bool { + return lhs.stableId < rhs.stableId + } + + func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { + let arguments = arguments as! WinterGramSettingsArguments + switch self { + case .ghostHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "GHOST MODE", sectionId: self.section) + case let .ghostEnabled(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Ghost Mode", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.ghostModeEnabled = value; return s } + }) + case let .ghostReadReceipts(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Send Read Receipts", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.sendReadReceipts = value; return s } + }) + case let .ghostReadStories(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Send Story Views", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.sendReadStories = value; return s } + }) + case let .ghostOnlineStatus(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Send Online Status", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.sendOnlineStatus = value; return s } + }) + case let .ghostUploadProgress(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Send Typing & Upload Status", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.sendUploadProgress = value; return s } + }) + case let .ghostOfflineAfterOnline(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Go Offline After Online", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.sendOfflineAfterOnline = value; return s } + }) + case let .ghostMarkReadAfterAction(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Mark Read After Action", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.markReadAfterAction = value; return s } + }) + case let .ghostUseScheduled(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Use Scheduled Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.useScheduledMessages = value; return s } + }) + case let .ghostSendWithoutSound(value): + return ItemListDisclosureItem(presentationData: presentationData, title: "Send Without Sound", label: value, sectionId: self.section, style: .blocks, action: { + arguments.presentSendWithoutSound() + }) + case let .ghostSuggestBeforeStory(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Ask Before Viewing Stories", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.suggestGhostBeforeStory = value; return s } + }) + case .ghostFooter: + return ItemListTextItem(presentationData: presentationData, text: .plain("When Ghost Mode is on, WinterGram stops sending read receipts, online status and typing activity."), sectionId: self.section) + + case .historyHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "HISTORY", sectionId: self.section) + case let .historySaveDeleted(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Save Deleted Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.saveDeletedMessages = value; return s } + }) + case let .historySaveEdits(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Save Edit History", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.saveMessagesHistory = value; return s } + }) + case let .historySemiTransparent(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Dim Deleted Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.semiTransparentDeletedMessages = value; return s } + }) + case .historyFooter: + return ItemListTextItem(presentationData: presentationData, text: .plain("Deleted and edited messages are kept locally on this device only."), sectionId: self.section) + + case .stashHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "HIDDEN ARCHIVE", sectionId: self.section) + case let .stashMute(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Mute Notifications", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.stashMuteNotifications = value; return s } + }) + case let .stashAutoRead(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Auto Mark as Read", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.stashAutoMarkRead = value; return s } + }) + case .stashFooter: + return ItemListTextItem(presentationData: presentationData, text: .plain("Stashed chats are hidden from the main list and accessible only here."), sectionId: self.section) + + case .antiHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "FEATURES", sectionId: self.section) + case let .antiDisableAds(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Disable Ads", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.disableAds = value; return s } + }) + case let .antiLocalPremium(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Local Premium", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.localPremium = value; return s } + }) + case let .antiDisableStories(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Hide Stories", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.disableStories = value; return s } + }) + case let .antiHidePremiumStatuses(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Hide Premium Statuses", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.hidePremiumStatuses = value; return s } + }) + case let .antiDisableLinkWarning(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Disable Open Link Warning", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.disableOpenLinkWarning = value; return s } + }) + case .antiFooter: + return ItemListTextItem(presentationData: presentationData, text: .plain("Local Premium unlocks Premium-only UI on this device; it does not grant server-side Premium."), sectionId: self.section) + + case .confirmHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "SEND CONFIRMATIONS", sectionId: self.section) + case let .confirmStickers(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Confirm Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.stickerConfirmation = value; return s } + }) + case let .confirmGif(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Confirm GIFs", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.gifConfirmation = value; return s } + }) + case let .confirmVoice(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Confirm Voice Messages", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.voiceConfirmation = value; return s } + }) + + case .chatHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "CHAT", sectionId: self.section) + case let .chatShowSeconds(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Show Message Seconds", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.showMessageSeconds = value; return s } + }) + case let .chatShowPeerId(value): + return ItemListDisclosureItem(presentationData: presentationData, title: "Show Peer ID", label: value, sectionId: self.section, style: .blocks, action: { + arguments.presentPeerId() + }) + case let .chatTranslate(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Message Translation", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.translateMessages = value; return s } + }) + case let .chatTranslateProvider(value): + return ItemListDisclosureItem(presentationData: presentationData, title: "Translation Provider", label: value, sectionId: self.section, style: .blocks, action: { + arguments.presentTranslationProvider() + }) + case let .chatWebviewPlatform(value): + return ItemListDisclosureItem(presentationData: presentationData, title: "WebView Platform", label: value, sectionId: self.section, style: .blocks, action: { + arguments.presentWebviewPlatform() + }) + case let .chatWebviewHeight(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Increase WebView Height", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.increaseWebviewHeight = value; return s } + }) + case let .chatOnlyAddedEmoji(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Only Added Emoji & Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.showOnlyAddedEmojisAndStickers = value; return s } + }) + + case .appearanceHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "APPEARANCE", sectionId: self.section) + case let .appearanceMaterial(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Material Design", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.materialDesign = value; return s } + }) + case let .appearanceSingleCorner(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Single Corner Radius", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.singleCornerRadius = value; return s } + }) + case let .appearanceIconPack(value): + return ItemListDisclosureItem(presentationData: presentationData, title: "Icon Pack", label: value, sectionId: self.section, style: .blocks, action: { + arguments.presentIconPack() + }) + + case .glassHeader: + return ItemListSectionHeaderItem(presentationData: presentationData, text: "LIQUID GLASS", sectionId: self.section) + case let .glassEnabled(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Liquid Glass", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.enabled = value; return s } + }) + case let .glassVibrancy(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Vibrancy", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.vibrancy = value; return s } + }) + case let .glassChatList(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Chat List", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.applyToChatList = value; return s } + }) + case let .glassNavBars(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Navigation Bars", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.applyToNavigationBars = value; return s } + }) + case let .glassTabBar(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Tab Bar", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.applyToTabBar = value; return s } + }) + case let .glassBubbles(value): + return ItemListSwitchItem(presentationData: presentationData, title: "Apply to Bubbles", value: value, sectionId: self.section, style: .blocks, updated: { value in + arguments.updateSettings { var s = $0; s.liquidGlass.applyToBubbles = value; return s } + }) + case .glassFooter: + return ItemListTextItem(presentationData: presentationData, text: .plain("Transparency, blur and tint can be fine-tuned per surface. Turn Liquid Glass off for the standard opaque look."), sectionId: self.section) + } + } +} + +private func sendWithoutSoundLabel(_ value: WinterGramSendWithoutSound) -> String { + switch value { + case .never: return "Never" + case .inGhostMode: return "In Ghost Mode" + case .always: return "Always" + } +} + +private func peerIdLabel(_ value: WinterGramPeerIdDisplay) -> String { + switch value { + case .hidden: return "Hidden" + case .telegramApi: return "Telegram API" + case .botApi: return "Bot API" + } +} + +private func translationProviderLabel(_ value: WinterGramTranslationProvider) -> String { + switch value { + case .telegram: return "Telegram" + case .google: return "Google" + case .yandex: return "Yandex" + case .system: return "System" + } +} + +private func webviewPlatformLabel(_ value: WinterGramWebviewPlatform) -> String { + switch value { + case .auto: return "Automatic" + case .ios: return "iOS" + case .android: return "Android" + case .macos: return "macOS" + case .desktop: return "Desktop" + } +} + +private func iconPackLabel(_ value: WinterGramIconPack) -> String { + switch value { + case .wintergram: return "WinterGram" + case .ayugram: return "AyuGram" + case .exteragram: return "exteraGram" + case .telegram: return "Telegram" + } +} + +private func winterGramSettingsEntries(settings: WinterGramSettings) -> [WinterGramSettingsEntry] { + var entries: [WinterGramSettingsEntry] = [] + + entries.append(.ghostHeader) + entries.append(.ghostEnabled(settings.ghostModeEnabled)) + entries.append(.ghostReadReceipts(settings.sendReadReceipts)) + entries.append(.ghostReadStories(settings.sendReadStories)) + entries.append(.ghostOnlineStatus(settings.sendOnlineStatus)) + entries.append(.ghostUploadProgress(settings.sendUploadProgress)) + entries.append(.ghostOfflineAfterOnline(settings.sendOfflineAfterOnline)) + entries.append(.ghostMarkReadAfterAction(settings.markReadAfterAction)) + entries.append(.ghostUseScheduled(settings.useScheduledMessages)) + entries.append(.ghostSendWithoutSound(sendWithoutSoundLabel(settings.sendWithoutSound))) + entries.append(.ghostSuggestBeforeStory(settings.suggestGhostBeforeStory)) + entries.append(.ghostFooter) + + entries.append(.historyHeader) + entries.append(.historySaveDeleted(settings.saveDeletedMessages)) + entries.append(.historySaveEdits(settings.saveMessagesHistory)) + entries.append(.historySemiTransparent(settings.semiTransparentDeletedMessages)) + entries.append(.historyFooter) + + entries.append(.stashHeader) + entries.append(.stashMute(settings.stashMuteNotifications)) + entries.append(.stashAutoRead(settings.stashAutoMarkRead)) + entries.append(.stashFooter) + + entries.append(.antiHeader) + entries.append(.antiDisableAds(settings.disableAds)) + entries.append(.antiLocalPremium(settings.localPremium)) + entries.append(.antiDisableStories(settings.disableStories)) + entries.append(.antiHidePremiumStatuses(settings.hidePremiumStatuses)) + entries.append(.antiDisableLinkWarning(settings.disableOpenLinkWarning)) + entries.append(.antiFooter) + + entries.append(.confirmHeader) + entries.append(.confirmStickers(settings.stickerConfirmation)) + entries.append(.confirmGif(settings.gifConfirmation)) + entries.append(.confirmVoice(settings.voiceConfirmation)) + + entries.append(.chatHeader) + entries.append(.chatShowSeconds(settings.showMessageSeconds)) + entries.append(.chatShowPeerId(peerIdLabel(settings.showPeerId))) + entries.append(.chatTranslate(settings.translateMessages)) + entries.append(.chatTranslateProvider(translationProviderLabel(settings.translationProvider))) + entries.append(.chatWebviewPlatform(webviewPlatformLabel(settings.webviewSpoofPlatform))) + entries.append(.chatWebviewHeight(settings.increaseWebviewHeight)) + entries.append(.chatOnlyAddedEmoji(settings.showOnlyAddedEmojisAndStickers)) + + entries.append(.appearanceHeader) + entries.append(.appearanceMaterial(settings.materialDesign)) + entries.append(.appearanceSingleCorner(settings.singleCornerRadius)) + entries.append(.appearanceIconPack(iconPackLabel(settings.iconPack))) + + entries.append(.glassHeader) + entries.append(.glassEnabled(settings.liquidGlass.enabled)) + entries.append(.glassVibrancy(settings.liquidGlass.vibrancy)) + entries.append(.glassChatList(settings.liquidGlass.applyToChatList)) + entries.append(.glassNavBars(settings.liquidGlass.applyToNavigationBars)) + entries.append(.glassTabBar(settings.liquidGlass.applyToTabBar)) + entries.append(.glassBubbles(settings.liquidGlass.applyToBubbles)) + entries.append(.glassFooter) + + return entries +} + +public func winterGramSettingsController(context: AccountContext) -> ViewController { + let accountManager = context.sharedContext.accountManager + + var presentControllerImpl: ((ViewController, Any?) -> Void)? + + let updateSettings: (@escaping (WinterGramSettings) -> WinterGramSettings) -> Void = { f in + let _ = updateWinterGramSettingsInteractively(accountManager: accountManager, f).start() + } + + func presentChoice(title: String, options: [(String, T)], apply: @escaping (T, WinterGramSettings) -> WinterGramSettings) { + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + let controller = ActionSheetController(presentationData: presentationData) + var items: [ActionSheetItem] = [] + items.append(ActionSheetTextItem(title: title)) + for (label, value) in options { + items.append(ActionSheetButtonItem(title: label, action: { [weak controller] in + controller?.dismissAnimated() + updateSettings { apply(value, $0) } + })) + } + controller.setItemGroups([ + ActionSheetItemGroup(items: items), + ActionSheetItemGroup(items: [ + ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak controller] in + controller?.dismissAnimated() + }) + ]) + ]) + presentControllerImpl?(controller, nil) + } + + let arguments = WinterGramSettingsArguments( + updateSettings: updateSettings, + presentSendWithoutSound: { + presentChoice(title: "Send Without Sound", options: [ + ("Never", WinterGramSendWithoutSound.never), + ("In Ghost Mode", WinterGramSendWithoutSound.inGhostMode), + ("Always", WinterGramSendWithoutSound.always) + ], apply: { value, settings in + var settings = settings + settings.sendWithoutSound = value + return settings + }) + }, + presentPeerId: { + presentChoice(title: "Show Peer ID", options: [ + ("Hidden", WinterGramPeerIdDisplay.hidden), + ("Telegram API", WinterGramPeerIdDisplay.telegramApi), + ("Bot API", WinterGramPeerIdDisplay.botApi) + ], apply: { value, settings in + var settings = settings + settings.showPeerId = value + return settings + }) + }, + presentTranslationProvider: { + presentChoice(title: "Translation Provider", options: [ + ("Telegram", WinterGramTranslationProvider.telegram), + ("Google", WinterGramTranslationProvider.google), + ("Yandex", WinterGramTranslationProvider.yandex), + ("System", WinterGramTranslationProvider.system) + ], apply: { value, settings in + var settings = settings + settings.translationProvider = value + return settings + }) + }, + presentWebviewPlatform: { + presentChoice(title: "WebView Platform", options: [ + ("Automatic", WinterGramWebviewPlatform.auto), + ("iOS", WinterGramWebviewPlatform.ios), + ("Android", WinterGramWebviewPlatform.android), + ("macOS", WinterGramWebviewPlatform.macos), + ("Desktop", WinterGramWebviewPlatform.desktop) + ], apply: { value, settings in + var settings = settings + settings.webviewSpoofPlatform = value + return settings + }) + }, + presentIconPack: { + presentChoice(title: "Icon Pack", options: [ + ("WinterGram", WinterGramIconPack.wintergram), + ("AyuGram", WinterGramIconPack.ayugram), + ("exteraGram", WinterGramIconPack.exteragram), + ("Telegram", WinterGramIconPack.telegram) + ], apply: { value, settings in + var settings = settings + settings.iconPack = value + return settings + }) + } + ) + + let signal = combineLatest(queue: .mainQueue(), + context.sharedContext.presentationData, + winterGramSettings(accountManager: accountManager) + ) + |> deliverOnMainQueue + |> map { presentationData, settings -> (ItemListControllerState, (ItemListNodeState, Any)) in + let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text("WinterGram"), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back)) + let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: winterGramSettingsEntries(settings: settings), style: .blocks, animateChanges: true) + return (controllerState, (listState, arguments)) + } + + let controller = ItemListController(context: context, state: signal) + presentControllerImpl = { [weak controller] c, a in + controller?.present(c, in: .window(.root), with: a) + } + return controller +} diff --git a/submodules/TelegramCore/Sources/Account/AccountManager.swift b/submodules/TelegramCore/Sources/Account/AccountManager.swift index 334e675922..24f9d915ae 100644 --- a/submodules/TelegramCore/Sources/Account/AccountManager.swift +++ b/submodules/TelegramCore/Sources/Account/AccountManager.swift @@ -279,8 +279,10 @@ public func performAppGroupUpgrades(appGroupPath: String, rootPath: String) { } do { + // WinterGram: include account data in the device's iCloud/iTunes backup so accounts + // survive a restore. Tradeoff: session auth keys are then part of the iCloud backup. var resourceValues = URLResourceValues() - resourceValues.isExcludedFromBackup = true + resourceValues.isExcludedFromBackup = false var mutableUrl = URL(fileURLWithPath: rootPath) try mutableUrl.setResourceValues(resourceValues) } catch let e { diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift index 2cd47b82f8..0c7e9317a8 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesSettings.swift @@ -131,7 +131,8 @@ public struct PresentationResourcesSettings { public static let powerSaving = renderSettingsIcon(name: "Item List/Icons/PowerSaving", backgroundColors: [colorOrange]) public static let business = renderSettingsIcon(name: "Item List/Icons/Business", backgroundColors: [UIColor(rgb: 0xA95CE3), UIColor(rgb: 0xF16B80)]) public static let myProfile = renderSettingsIcon(name: "Item List/Icons/Profile", backgroundColors: [colorRed]) - + public static let winterGram = renderSettingsIcon(name: "Item List/Icons/WinterGram", backgroundColors: [colorLightBlue]) + public static let birthday = renderSettingsIcon(name: "Item List/Icons/Cake", backgroundColors: [colorBlue]) public static let aiTools = renderSettingsIcon(name: "Item List/Icons/AITools", backgroundColors: [colorPurple]) public static let yourColor = renderSettingsIcon(name: "Item List/Icons/Brush", backgroundColors: [colorLightBlue]) diff --git a/submodules/TelegramUI/Components/OpenUserGeneratedUrl/BUILD b/submodules/TelegramUI/Components/OpenUserGeneratedUrl/BUILD index 524bf1429b..7dccc88142 100644 --- a/submodules/TelegramUI/Components/OpenUserGeneratedUrl/BUILD +++ b/submodules/TelegramUI/Components/OpenUserGeneratedUrl/BUILD @@ -18,6 +18,7 @@ swift_library( "//submodules/OverlayStatusController", "//submodules/UrlWhitelist", "//submodules/TelegramPresentationData", + "//submodules/TelegramUIPreferences", "//submodules/TelegramUI/Components/AlertComponent", "//submodules/TelegramUI/Components/AlertComponent/AlertCheckComponent", "//submodules/TelegramUI/Components/AlertComponent/AlertWebpagePreviewComponent", diff --git a/submodules/TelegramUI/Components/OpenUserGeneratedUrl/Sources/OpenUserGeneratedUrl.swift b/submodules/TelegramUI/Components/OpenUserGeneratedUrl/Sources/OpenUserGeneratedUrl.swift index 51d69bcbb9..316263fdde 100644 --- a/submodules/TelegramUI/Components/OpenUserGeneratedUrl/Sources/OpenUserGeneratedUrl.swift +++ b/submodules/TelegramUI/Components/OpenUserGeneratedUrl/Sources/OpenUserGeneratedUrl.swift @@ -8,6 +8,7 @@ import AccountContext import OverlayStatusController import UrlWhitelist import TelegramPresentationData +import TelegramUIPreferences import AlertComponent import AlertCheckComponent import AlertWebpagePreviewComponent @@ -111,7 +112,7 @@ public func openUserGeneratedUrl( } } - if concealed && !skipConcealedAlert { + if concealed && !skipConcealedAlert && !currentWinterGramSettings.disableOpenLinkWarning { var rawDisplayUrl: String = parsedString let maxLength = 180 if rawDisplayUrl.count > maxLength { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 51cc9be59f..dd9b4294a1 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -188,6 +188,7 @@ enum PeerInfoSettingsSection { case premiumManagement case stars case ton + case winterGram } enum PeerInfoReportType { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift index 806e652ac2..39f95aea3e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreenSettingsActions.swift @@ -217,15 +217,15 @@ extension PeerInfoScreenNode { guard let strongSelf = self else { return } - var maximumAvailableAccounts: Int = 3 + var maximumAvailableAccounts: Int = maximumNumberOfAccounts if accountAndPeer?.1.isPremium == true && !strongSelf.context.account.testingEnvironment { - maximumAvailableAccounts = 4 + maximumAvailableAccounts = maximumPremiumNumberOfAccounts } var count: Int = 1 for (accountContext, peer, _) in accountsAndPeers { if !accountContext.account.testingEnvironment { if peer.isPremium { - maximumAvailableAccounts = 4 + maximumAvailableAccounts = maximumPremiumNumberOfAccounts } count += 1 } @@ -271,6 +271,8 @@ extension PeerInfoScreenNode { self.interaction.editingOpenNameColorSetup() case .powerSaving: push(energySavingSettingsScreen(context: self.context)) + case .winterGram: + push(winterGramSettingsController(context: self.context)) case .businessSetup: guard let controller = self.controller, !controller.presentAccountFrozenInfoIfNeeded() else { return diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift index c5f58f4bb5..8d490dd027 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoSettingsItems.swift @@ -14,6 +14,7 @@ import TelegramStringFormatting import PeerNameColorItem enum SettingsSection: Int, CaseIterable { + case winterGram case edit case phone case accounts @@ -36,7 +37,11 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat for section in SettingsSection.allCases { items[section] = [] } - + + items[.winterGram]!.append(PeerInfoScreenDisclosureItem(id: 0, text: "WinterGram", icon: PresentationResourcesSettings.winterGram, action: { + interaction.openSettings(.winterGram) + })) + let setPhotoTitle: String if let peer = data.peer, !peer.profileImageRepresentations.isEmpty { setPhotoTitle = presentationData.strings.Settings_ChangeProfilePhoto @@ -239,7 +244,7 @@ func settingsItems(data: PeerInfoScreenData?, context: AccountContext, presentat items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 3, text: presentationData.strings.Settings_Appearance, icon: PresentationResourcesSettings.appearance, action: { interaction.openSettings(.appearance) })) - + items[.advanced]!.append(PeerInfoScreenDisclosureItem(id: 6, label: .text(data.isPowerSavingEnabled == true ? presentationData.strings.Settings_PowerSavingOn : presentationData.strings.Settings_PowerSavingOff), text: presentationData.strings.Settings_PowerSaving, icon: PresentationResourcesSettings.powerSaving, action: { interaction.openSettings(.powerSaving) })) diff --git a/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/Contents.json new file mode 100644 index 0000000000..d3e59ac636 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "wintergram_snowball_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/wintergram_snowball_30.pdf b/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/wintergram_snowball_30.pdf new file mode 100644 index 0000000000..fead9c9762 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Item List/Icons/WinterGram.imageset/wintergram_snowball_30.pdf differ diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index b1ea96b8da..2375483275 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -56,6 +56,17 @@ private let handleVoipNotifications = false private var testIsLaunched = false +func normalizeWinterGramUrlScheme(_ url: URL) -> URL { + guard let scheme = url.scheme, scheme.lowercased() == "wnt" else { + return url + } + guard var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { + return url + } + components.scheme = "tg" + return components.url ?? url +} + private func isKeyboardWindow(window: NSObject) -> Bool { let typeName = NSStringFromClass(type(of: window)) if #available(iOS 9.0, *) { @@ -248,7 +259,8 @@ private func extractAccountManagerState(records: AccountRecordsView (SharedApplicationContext, LoggingSettings) in return (sharedApplicationContext, transaction.getSharedData(SharedDataKeys.loggingSettings)?.get(LoggingSettings.self) ?? LoggingSettings.defaultSettings) @@ -1513,10 +1526,10 @@ private func extractAccountManagerState(records: AccountRecordsView take(1) |> mapToSignal { sharedApplicationContext -> Signal<(SharedAccountContextImpl, AuthorizedApplicationContext?, UnauthorizedApplicationContext?), NoError> in diff --git a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift index 6cac18a9de..8bda922c31 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryEntriesForView.swift @@ -6,6 +6,7 @@ import TemporaryCachedPeerDataManager import Emoji import AccountContext import TelegramPresentationData +import TelegramUIPreferences import ChatHistoryEntry import ChatMessageItemCommon import TextFormat @@ -687,7 +688,7 @@ func chatHistoryEntriesForView( } if view.laterId == nil && !view.isLoading { - if !entries.isEmpty, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage { + if !entries.isEmpty, !currentWinterGramSettings.disableAds, case let .MessageEntry(lastMessage, _, _, _, _, _) = entries[entries.count - 1], let message = adMessage { var nextAdMessageId: Int32 = 10000 let updatedMessage = Message( stableId: ChatHistoryListNodeImpl.fixedAdMessageStableId, diff --git a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift index 8961fa49e0..ce5d8d1b48 100644 --- a/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift +++ b/submodules/TelegramUIPreferences/Sources/PostboxKeys.swift @@ -51,6 +51,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 { case mediaDisplaySettings = 20 case updateSettings = 21 case chatSettings = 22 + case winterGramSettings = 23 } public struct ApplicationSpecificSharedDataKeys { @@ -77,6 +78,7 @@ public struct ApplicationSpecificSharedDataKeys { public static let mediaDisplaySettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.mediaDisplaySettings.rawValue) public static let updateSettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.updateSettings.rawValue) public static let chatSettings: EngineDataBuffer = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.chatSettings.rawValue) + public static let winterGramSettings: EngineDataBuffer = applicationSpecificSharedDataKey(ApplicationSpecificSharedDataKeyValues.winterGramSettings.rawValue) } private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 { diff --git a/submodules/TelegramUIPreferences/Sources/WinterGramSettings.swift b/submodules/TelegramUIPreferences/Sources/WinterGramSettings.swift new file mode 100644 index 0000000000..05ec0bbd38 --- /dev/null +++ b/submodules/TelegramUIPreferences/Sources/WinterGramSettings.swift @@ -0,0 +1,491 @@ +import Foundation +import TelegramCore +import SwiftSignalKit + +public enum WinterGramSendWithoutSound: Int32, Codable { + case never = 0 + case inGhostMode = 1 + case always = 2 +} + +public enum WinterGramPeerIdDisplay: Int32, Codable { + case hidden = 0 + case telegramApi = 1 + case botApi = 2 +} + +public enum WinterGramTranslationProvider: Int32, Codable { + case telegram = 0 + case google = 1 + case yandex = 2 + case system = 3 +} + +public enum WinterGramWebviewPlatform: Int32, Codable { + case auto = 0 + case ios = 1 + case android = 2 + case macos = 3 + case desktop = 4 +} + +public enum WinterGramIconPack: Int32, Codable { + case wintergram = 0 + case ayugram = 1 + case exteragram = 2 + case telegram = 3 +} + +public struct WinterGramLiquidGlass: Codable, Equatable { + public var enabled: Bool + public var transparency: Double + public var blurRadius: Double + public var tintColor: Int32? + public var vibrancy: Bool + public var applyToChatList: Bool + public var applyToBubbles: Bool + public var applyToNavigationBars: Bool + public var applyToTabBar: Bool + + public static var defaultSettings: WinterGramLiquidGlass { + return WinterGramLiquidGlass( + enabled: true, + transparency: 0.75, + blurRadius: 30.0, + tintColor: nil, + vibrancy: true, + applyToChatList: true, + applyToBubbles: false, + applyToNavigationBars: true, + applyToTabBar: true + ) + } + + public init( + enabled: Bool, + transparency: Double, + blurRadius: Double, + tintColor: Int32?, + vibrancy: Bool, + applyToChatList: Bool, + applyToBubbles: Bool, + applyToNavigationBars: Bool, + applyToTabBar: Bool + ) { + self.enabled = enabled + self.transparency = transparency + self.blurRadius = blurRadius + self.tintColor = tintColor + self.vibrancy = vibrancy + self.applyToChatList = applyToChatList + self.applyToBubbles = applyToBubbles + self.applyToNavigationBars = applyToNavigationBars + self.applyToTabBar = applyToTabBar + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + self.enabled = try container.decodeIfPresent(Bool.self, forKey: "enabled") ?? true + self.transparency = try container.decodeIfPresent(Double.self, forKey: "transparency") ?? 0.75 + self.blurRadius = try container.decodeIfPresent(Double.self, forKey: "blurRadius") ?? 30.0 + self.tintColor = try container.decodeIfPresent(Int32.self, forKey: "tintColor") + self.vibrancy = try container.decodeIfPresent(Bool.self, forKey: "vibrancy") ?? true + self.applyToChatList = try container.decodeIfPresent(Bool.self, forKey: "applyToChatList") ?? true + self.applyToBubbles = try container.decodeIfPresent(Bool.self, forKey: "applyToBubbles") ?? false + self.applyToNavigationBars = try container.decodeIfPresent(Bool.self, forKey: "applyToNavigationBars") ?? true + self.applyToTabBar = try container.decodeIfPresent(Bool.self, forKey: "applyToTabBar") ?? true + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + try container.encode(self.enabled, forKey: "enabled") + try container.encode(self.transparency, forKey: "transparency") + try container.encode(self.blurRadius, forKey: "blurRadius") + try container.encodeIfPresent(self.tintColor, forKey: "tintColor") + try container.encode(self.vibrancy, forKey: "vibrancy") + try container.encode(self.applyToChatList, forKey: "applyToChatList") + try container.encode(self.applyToBubbles, forKey: "applyToBubbles") + try container.encode(self.applyToNavigationBars, forKey: "applyToNavigationBars") + try container.encode(self.applyToTabBar, forKey: "applyToTabBar") + } +} + +public struct WinterGramSettings: Codable, Equatable { + // Ghost mode & privacy + public var ghostModeEnabled: Bool + public var sendReadReceipts: Bool + public var sendReadStories: Bool + public var sendOnlineStatus: Bool + public var sendUploadProgress: Bool + public var sendOfflineAfterOnline: Bool + public var markReadAfterAction: Bool + public var useScheduledMessages: Bool + public var sendWithoutSound: WinterGramSendWithoutSound + public var suggestGhostBeforeStory: Bool + + // Local history + public var saveDeletedMessages: Bool + public var saveMessagesHistory: Bool + public var saveForBots: Bool + public var hideFromBlocked: Bool + public var semiTransparentDeletedMessages: Bool + + // Hidden archive ("ААрхив") + public var stashedPeerIds: [Int64] + public var stashMuteNotifications: Bool + public var stashAutoMarkRead: Bool + + // Anti-features + public var disableAds: Bool + public var localPremium: Bool + public var shadowBanIds: [Int64] + public var disableStories: Bool + public var hidePremiumStatuses: Bool + public var disableOpenLinkWarning: Bool + + // Confirmations + public var stickerConfirmation: Bool + public var gifConfirmation: Bool + public var voiceConfirmation: Bool + + // Message decorations + public var showMessageSeconds: Bool + public var showPeerId: WinterGramPeerIdDisplay + public var deletedMark: String + public var editedMark: String + public var recentStickersCount: Int32 + + // Translation + public var translateMessages: Bool + public var translationProvider: WinterGramTranslationProvider + + // Webview / platform spoofing + public var webviewSpoofPlatform: WinterGramWebviewPlatform + public var increaseWebviewHeight: Bool + + // Appearance & customization + public var liquidGlass: WinterGramLiquidGlass + public var materialDesign: Bool + public var avatarCornerRadius: Int32 + public var singleCornerRadius: Bool + public var messageBubbleRadius: Int32 + public var removeMessageTail: Bool + public var customFont: String? + public var monoFont: String? + public var appIcon: String + public var iconPack: WinterGramIconPack + public var showOnlyAddedEmojisAndStickers: Bool + + public static var defaultSettings: WinterGramSettings { + return WinterGramSettings( + ghostModeEnabled: false, + sendReadReceipts: true, + sendReadStories: true, + sendOnlineStatus: true, + sendUploadProgress: true, + sendOfflineAfterOnline: false, + markReadAfterAction: true, + useScheduledMessages: false, + sendWithoutSound: .never, + suggestGhostBeforeStory: true, + saveDeletedMessages: true, + saveMessagesHistory: true, + saveForBots: false, + hideFromBlocked: false, + semiTransparentDeletedMessages: false, + stashedPeerIds: [], + stashMuteNotifications: true, + stashAutoMarkRead: false, + disableAds: true, + localPremium: false, + shadowBanIds: [], + disableStories: false, + hidePremiumStatuses: false, + disableOpenLinkWarning: false, + stickerConfirmation: false, + gifConfirmation: false, + voiceConfirmation: false, + showMessageSeconds: false, + showPeerId: .botApi, + deletedMark: "🧹", + editedMark: "", + recentStickersCount: 100, + translateMessages: false, + translationProvider: .telegram, + webviewSpoofPlatform: .auto, + increaseWebviewHeight: false, + liquidGlass: .defaultSettings, + materialDesign: true, + avatarCornerRadius: 50, + singleCornerRadius: false, + messageBubbleRadius: 16, + removeMessageTail: false, + customFont: nil, + monoFont: nil, + appIcon: "WinterGramDark", + iconPack: .wintergram, + showOnlyAddedEmojisAndStickers: false + ) + } + + public init( + ghostModeEnabled: Bool, + sendReadReceipts: Bool, + sendReadStories: Bool, + sendOnlineStatus: Bool, + sendUploadProgress: Bool, + sendOfflineAfterOnline: Bool, + markReadAfterAction: Bool, + useScheduledMessages: Bool, + sendWithoutSound: WinterGramSendWithoutSound, + suggestGhostBeforeStory: Bool, + saveDeletedMessages: Bool, + saveMessagesHistory: Bool, + saveForBots: Bool, + hideFromBlocked: Bool, + semiTransparentDeletedMessages: Bool, + stashedPeerIds: [Int64], + stashMuteNotifications: Bool, + stashAutoMarkRead: Bool, + disableAds: Bool, + localPremium: Bool, + shadowBanIds: [Int64], + disableStories: Bool, + hidePremiumStatuses: Bool, + disableOpenLinkWarning: Bool, + stickerConfirmation: Bool, + gifConfirmation: Bool, + voiceConfirmation: Bool, + showMessageSeconds: Bool, + showPeerId: WinterGramPeerIdDisplay, + deletedMark: String, + editedMark: String, + recentStickersCount: Int32, + translateMessages: Bool, + translationProvider: WinterGramTranslationProvider, + webviewSpoofPlatform: WinterGramWebviewPlatform, + increaseWebviewHeight: Bool, + liquidGlass: WinterGramLiquidGlass, + materialDesign: Bool, + avatarCornerRadius: Int32, + singleCornerRadius: Bool, + messageBubbleRadius: Int32, + removeMessageTail: Bool, + customFont: String?, + monoFont: String?, + appIcon: String, + iconPack: WinterGramIconPack, + showOnlyAddedEmojisAndStickers: Bool + ) { + self.ghostModeEnabled = ghostModeEnabled + self.sendReadReceipts = sendReadReceipts + self.sendReadStories = sendReadStories + self.sendOnlineStatus = sendOnlineStatus + self.sendUploadProgress = sendUploadProgress + self.sendOfflineAfterOnline = sendOfflineAfterOnline + self.markReadAfterAction = markReadAfterAction + self.useScheduledMessages = useScheduledMessages + self.sendWithoutSound = sendWithoutSound + self.suggestGhostBeforeStory = suggestGhostBeforeStory + self.saveDeletedMessages = saveDeletedMessages + self.saveMessagesHistory = saveMessagesHistory + self.saveForBots = saveForBots + self.hideFromBlocked = hideFromBlocked + self.semiTransparentDeletedMessages = semiTransparentDeletedMessages + self.stashedPeerIds = stashedPeerIds + self.stashMuteNotifications = stashMuteNotifications + self.stashAutoMarkRead = stashAutoMarkRead + self.disableAds = disableAds + self.localPremium = localPremium + self.shadowBanIds = shadowBanIds + self.disableStories = disableStories + self.hidePremiumStatuses = hidePremiumStatuses + self.disableOpenLinkWarning = disableOpenLinkWarning + self.stickerConfirmation = stickerConfirmation + self.gifConfirmation = gifConfirmation + self.voiceConfirmation = voiceConfirmation + self.showMessageSeconds = showMessageSeconds + self.showPeerId = showPeerId + self.deletedMark = deletedMark + self.editedMark = editedMark + self.recentStickersCount = recentStickersCount + self.translateMessages = translateMessages + self.translationProvider = translationProvider + self.webviewSpoofPlatform = webviewSpoofPlatform + self.increaseWebviewHeight = increaseWebviewHeight + self.liquidGlass = liquidGlass + self.materialDesign = materialDesign + self.avatarCornerRadius = avatarCornerRadius + self.singleCornerRadius = singleCornerRadius + self.messageBubbleRadius = messageBubbleRadius + self.removeMessageTail = removeMessageTail + self.customFont = customFont + self.monoFont = monoFont + self.appIcon = appIcon + self.iconPack = iconPack + self.showOnlyAddedEmojisAndStickers = showOnlyAddedEmojisAndStickers + } + + public init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: StringCodingKey.self) + let defaults = WinterGramSettings.defaultSettings + + self.ghostModeEnabled = try container.decodeIfPresent(Bool.self, forKey: "ghostModeEnabled") ?? defaults.ghostModeEnabled + self.sendReadReceipts = try container.decodeIfPresent(Bool.self, forKey: "sendReadReceipts") ?? defaults.sendReadReceipts + self.sendReadStories = try container.decodeIfPresent(Bool.self, forKey: "sendReadStories") ?? defaults.sendReadStories + self.sendOnlineStatus = try container.decodeIfPresent(Bool.self, forKey: "sendOnlineStatus") ?? defaults.sendOnlineStatus + self.sendUploadProgress = try container.decodeIfPresent(Bool.self, forKey: "sendUploadProgress") ?? defaults.sendUploadProgress + self.sendOfflineAfterOnline = try container.decodeIfPresent(Bool.self, forKey: "sendOfflineAfterOnline") ?? defaults.sendOfflineAfterOnline + self.markReadAfterAction = try container.decodeIfPresent(Bool.self, forKey: "markReadAfterAction") ?? defaults.markReadAfterAction + self.useScheduledMessages = try container.decodeIfPresent(Bool.self, forKey: "useScheduledMessages") ?? defaults.useScheduledMessages + self.sendWithoutSound = try container.decodeIfPresent(WinterGramSendWithoutSound.self, forKey: "sendWithoutSound") ?? defaults.sendWithoutSound + self.suggestGhostBeforeStory = try container.decodeIfPresent(Bool.self, forKey: "suggestGhostBeforeStory") ?? defaults.suggestGhostBeforeStory + self.saveDeletedMessages = try container.decodeIfPresent(Bool.self, forKey: "saveDeletedMessages") ?? defaults.saveDeletedMessages + self.saveMessagesHistory = try container.decodeIfPresent(Bool.self, forKey: "saveMessagesHistory") ?? defaults.saveMessagesHistory + self.saveForBots = try container.decodeIfPresent(Bool.self, forKey: "saveForBots") ?? defaults.saveForBots + self.hideFromBlocked = try container.decodeIfPresent(Bool.self, forKey: "hideFromBlocked") ?? defaults.hideFromBlocked + self.semiTransparentDeletedMessages = try container.decodeIfPresent(Bool.self, forKey: "semiTransparentDeletedMessages") ?? defaults.semiTransparentDeletedMessages + self.stashedPeerIds = try container.decodeIfPresent([Int64].self, forKey: "stashedPeerIds") ?? defaults.stashedPeerIds + self.stashMuteNotifications = try container.decodeIfPresent(Bool.self, forKey: "stashMuteNotifications") ?? defaults.stashMuteNotifications + self.stashAutoMarkRead = try container.decodeIfPresent(Bool.self, forKey: "stashAutoMarkRead") ?? defaults.stashAutoMarkRead + self.disableAds = try container.decodeIfPresent(Bool.self, forKey: "disableAds") ?? defaults.disableAds + self.localPremium = try container.decodeIfPresent(Bool.self, forKey: "localPremium") ?? defaults.localPremium + self.shadowBanIds = try container.decodeIfPresent([Int64].self, forKey: "shadowBanIds") ?? defaults.shadowBanIds + self.disableStories = try container.decodeIfPresent(Bool.self, forKey: "disableStories") ?? defaults.disableStories + self.hidePremiumStatuses = try container.decodeIfPresent(Bool.self, forKey: "hidePremiumStatuses") ?? defaults.hidePremiumStatuses + self.disableOpenLinkWarning = try container.decodeIfPresent(Bool.self, forKey: "disableOpenLinkWarning") ?? defaults.disableOpenLinkWarning + self.stickerConfirmation = try container.decodeIfPresent(Bool.self, forKey: "stickerConfirmation") ?? defaults.stickerConfirmation + self.gifConfirmation = try container.decodeIfPresent(Bool.self, forKey: "gifConfirmation") ?? defaults.gifConfirmation + self.voiceConfirmation = try container.decodeIfPresent(Bool.self, forKey: "voiceConfirmation") ?? defaults.voiceConfirmation + self.showMessageSeconds = try container.decodeIfPresent(Bool.self, forKey: "showMessageSeconds") ?? defaults.showMessageSeconds + self.showPeerId = try container.decodeIfPresent(WinterGramPeerIdDisplay.self, forKey: "showPeerId") ?? defaults.showPeerId + self.deletedMark = try container.decodeIfPresent(String.self, forKey: "deletedMark") ?? defaults.deletedMark + self.editedMark = try container.decodeIfPresent(String.self, forKey: "editedMark") ?? defaults.editedMark + self.recentStickersCount = try container.decodeIfPresent(Int32.self, forKey: "recentStickersCount") ?? defaults.recentStickersCount + self.translateMessages = try container.decodeIfPresent(Bool.self, forKey: "translateMessages") ?? defaults.translateMessages + self.translationProvider = try container.decodeIfPresent(WinterGramTranslationProvider.self, forKey: "translationProvider") ?? defaults.translationProvider + self.webviewSpoofPlatform = try container.decodeIfPresent(WinterGramWebviewPlatform.self, forKey: "webviewSpoofPlatform") ?? defaults.webviewSpoofPlatform + self.increaseWebviewHeight = try container.decodeIfPresent(Bool.self, forKey: "increaseWebviewHeight") ?? defaults.increaseWebviewHeight + self.liquidGlass = try container.decodeIfPresent(WinterGramLiquidGlass.self, forKey: "liquidGlass") ?? defaults.liquidGlass + self.materialDesign = try container.decodeIfPresent(Bool.self, forKey: "materialDesign") ?? defaults.materialDesign + self.avatarCornerRadius = try container.decodeIfPresent(Int32.self, forKey: "avatarCornerRadius") ?? defaults.avatarCornerRadius + self.singleCornerRadius = try container.decodeIfPresent(Bool.self, forKey: "singleCornerRadius") ?? defaults.singleCornerRadius + self.messageBubbleRadius = try container.decodeIfPresent(Int32.self, forKey: "messageBubbleRadius") ?? defaults.messageBubbleRadius + self.removeMessageTail = try container.decodeIfPresent(Bool.self, forKey: "removeMessageTail") ?? defaults.removeMessageTail + self.customFont = try container.decodeIfPresent(String.self, forKey: "customFont") + self.monoFont = try container.decodeIfPresent(String.self, forKey: "monoFont") + self.appIcon = try container.decodeIfPresent(String.self, forKey: "appIcon") ?? defaults.appIcon + self.iconPack = try container.decodeIfPresent(WinterGramIconPack.self, forKey: "iconPack") ?? defaults.iconPack + self.showOnlyAddedEmojisAndStickers = try container.decodeIfPresent(Bool.self, forKey: "showOnlyAddedEmojisAndStickers") ?? defaults.showOnlyAddedEmojisAndStickers + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: StringCodingKey.self) + try container.encode(self.ghostModeEnabled, forKey: "ghostModeEnabled") + try container.encode(self.sendReadReceipts, forKey: "sendReadReceipts") + try container.encode(self.sendReadStories, forKey: "sendReadStories") + try container.encode(self.sendOnlineStatus, forKey: "sendOnlineStatus") + try container.encode(self.sendUploadProgress, forKey: "sendUploadProgress") + try container.encode(self.sendOfflineAfterOnline, forKey: "sendOfflineAfterOnline") + try container.encode(self.markReadAfterAction, forKey: "markReadAfterAction") + try container.encode(self.useScheduledMessages, forKey: "useScheduledMessages") + try container.encode(self.sendWithoutSound, forKey: "sendWithoutSound") + try container.encode(self.suggestGhostBeforeStory, forKey: "suggestGhostBeforeStory") + try container.encode(self.saveDeletedMessages, forKey: "saveDeletedMessages") + try container.encode(self.saveMessagesHistory, forKey: "saveMessagesHistory") + try container.encode(self.saveForBots, forKey: "saveForBots") + try container.encode(self.hideFromBlocked, forKey: "hideFromBlocked") + try container.encode(self.semiTransparentDeletedMessages, forKey: "semiTransparentDeletedMessages") + try container.encode(self.stashedPeerIds, forKey: "stashedPeerIds") + try container.encode(self.stashMuteNotifications, forKey: "stashMuteNotifications") + try container.encode(self.stashAutoMarkRead, forKey: "stashAutoMarkRead") + try container.encode(self.disableAds, forKey: "disableAds") + try container.encode(self.localPremium, forKey: "localPremium") + try container.encode(self.shadowBanIds, forKey: "shadowBanIds") + try container.encode(self.disableStories, forKey: "disableStories") + try container.encode(self.hidePremiumStatuses, forKey: "hidePremiumStatuses") + try container.encode(self.disableOpenLinkWarning, forKey: "disableOpenLinkWarning") + try container.encode(self.stickerConfirmation, forKey: "stickerConfirmation") + try container.encode(self.gifConfirmation, forKey: "gifConfirmation") + try container.encode(self.voiceConfirmation, forKey: "voiceConfirmation") + try container.encode(self.showMessageSeconds, forKey: "showMessageSeconds") + try container.encode(self.showPeerId, forKey: "showPeerId") + try container.encode(self.deletedMark, forKey: "deletedMark") + try container.encode(self.editedMark, forKey: "editedMark") + try container.encode(self.recentStickersCount, forKey: "recentStickersCount") + try container.encode(self.translateMessages, forKey: "translateMessages") + try container.encode(self.translationProvider, forKey: "translationProvider") + try container.encode(self.webviewSpoofPlatform, forKey: "webviewSpoofPlatform") + try container.encode(self.increaseWebviewHeight, forKey: "increaseWebviewHeight") + try container.encode(self.liquidGlass, forKey: "liquidGlass") + try container.encode(self.materialDesign, forKey: "materialDesign") + try container.encode(self.avatarCornerRadius, forKey: "avatarCornerRadius") + try container.encode(self.singleCornerRadius, forKey: "singleCornerRadius") + try container.encode(self.messageBubbleRadius, forKey: "messageBubbleRadius") + try container.encode(self.removeMessageTail, forKey: "removeMessageTail") + try container.encodeIfPresent(self.customFont, forKey: "customFont") + try container.encodeIfPresent(self.monoFont, forKey: "monoFont") + try container.encode(self.appIcon, forKey: "appIcon") + try container.encode(self.iconPack, forKey: "iconPack") + try container.encode(self.showOnlyAddedEmojisAndStickers, forKey: "showOnlyAddedEmojisAndStickers") + } + + public func isShadowBanned(_ peerId: Int64) -> Bool { + return self.shadowBanIds.contains(peerId) + } + + public func isStashed(_ peerId: Int64) -> Bool { + return self.stashedPeerIds.contains(peerId) + } + + public var shouldSendWithoutSound: Bool { + switch self.sendWithoutSound { + case .never: + return false + case .inGhostMode: + return self.ghostModeEnabled + case .always: + return true + } + } +} + +public func updateWinterGramSettingsInteractively(accountManager: AccountManager, _ f: @escaping (WinterGramSettings) -> WinterGramSettings) -> Signal { + return accountManager.transaction { transaction -> Void in + transaction.updateSharedData(ApplicationSpecificSharedDataKeys.winterGramSettings, { entry in + let currentSettings: WinterGramSettings + if let entry = entry?.get(WinterGramSettings.self) { + currentSettings = entry + } else { + currentSettings = .defaultSettings + } + return SharedPreferencesEntry(f(currentSettings)) + }) + } +} + +public func winterGramSettings(accountManager: AccountManager) -> Signal { + return accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.winterGramSettings]) + |> map { sharedData -> WinterGramSettings in + return sharedData.entries[ApplicationSpecificSharedDataKeys.winterGramSettings]?.get(WinterGramSettings.self) ?? .defaultSettings + } +} + +private let currentWinterGramSettingsValue = Atomic(value: .defaultSettings) + +// Synchronous snapshot of the latest settings, for hook points that cannot subscribe to a Signal. +// Kept up to date by observeWinterGramSettings(accountManager:) at app startup. +public var currentWinterGramSettings: WinterGramSettings { + return currentWinterGramSettingsValue.with { $0 } +} + +public func setCurrentWinterGramSettings(_ settings: WinterGramSettings) { + let _ = currentWinterGramSettingsValue.swap(settings) +} + +public func observeWinterGramSettings(accountManager: AccountManager) -> Disposable { + return (winterGramSettings(accountManager: accountManager) + |> deliverOnMainQueue).start(next: { settings in + setCurrentWinterGramSettings(settings) + }) +}