Compare commits
212 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 222a643ce3 | |||
| f583732688 | |||
| 4e9443e200 | |||
| a96f8d8ac3 | |||
| d4a22a5110 | |||
| adb5abf4fb | |||
| 4a6ae07409 | |||
| f6081db13f | |||
| 33b6f5d207 | |||
| 84c62470bb | |||
| a3f4185b45 | |||
| 2729fee0ac | |||
| 964f9bd904 | |||
| cfa007de84 | |||
| 77d52bb1b3 | |||
| 2f0b50cb37 | |||
| 6185fa980d | |||
| 1cce1e8a90 | |||
| 237baf11df | |||
| 46023f4260 | |||
| 8459c29073 | |||
| f072173d7c | |||
| bec34c34b1 | |||
| 04ab148b2e | |||
| e314abefb8 | |||
| cdaa23363f | |||
| 5401d00548 | |||
| c06699e8e7 | |||
| 2f40a44b5c | |||
| 0945e04f6b | |||
| db7c16f82b | |||
| 30548c2859 | |||
| 0163938e00 | |||
| 3421b656db | |||
| 39b80c98c7 | |||
| 323500f6dd | |||
| 11cf0486cb | |||
| 46579ac84d | |||
| f324c53440 | |||
| 87df90227e | |||
| 6f57302562 | |||
| d116c8fea0 | |||
| 32462fca9b | |||
| 55a174190e | |||
| 9f738cded2 | |||
| 87c1329490 | |||
| ded2015dc2 | |||
| 98a71cce89 | |||
| a3ba1ba2a1 | |||
| 42842619b0 | |||
| 32d3b90cdc | |||
| b182aeb51e | |||
| 7049929a59 | |||
| aa5413da4e | |||
| 32dfe0f65e | |||
| 5d33290218 | |||
| e47ff9fd34 | |||
| 58e38c487a | |||
| 852196a013 | |||
| 0ff3d4b2ed | |||
| b843dab87a | |||
| 1e34bc6b30 | |||
| b3225af685 | |||
| 59e44d4e96 | |||
| 9321f22a9e | |||
| 8718e0a045 | |||
| c4cf4325d6 | |||
| 9c71aba5d1 | |||
| 20bd7db4d9 | |||
| 92f0358800 | |||
| 0372f2be9c | |||
| 7dfb93f7c2 | |||
| 3c150d9742 | |||
| 3c5cace175 | |||
| 7e7fd6f1b4 | |||
| eebe7adbcb | |||
| 5561bdeb5e | |||
| a670095294 | |||
| 104ba4db7c | |||
| 9a6ab3b0f2 | |||
| e5b89b1572 | |||
| 65a80766f4 | |||
| 736b489eb7 | |||
| d3778f92d2 | |||
| 30e694420a | |||
| 091c13bc23 | |||
| 5130c5df80 | |||
| 7542f04010 | |||
| dff4191ac9 | |||
| 15e2874da8 | |||
| 8eb49d5efc | |||
| 230761eb23 | |||
| bb0ab27244 | |||
| bdeaf4f27a | |||
| 6d608bed98 | |||
| 5a47acf1d2 | |||
| 02a306ba70 | |||
| 75e19235cd | |||
| 7d9b4e1e03 | |||
| d0fff1611c | |||
| e4ca8ae847 | |||
| f53b64bc72 | |||
| 981909e567 | |||
| ce6fc19b84 | |||
| a38f731265 | |||
| d55dd7dffd | |||
| 37478d951a | |||
| 814de17f28 | |||
| 5f97d683df | |||
| 95d4e02ae1 | |||
| abf9764640 | |||
| 80ee38f9fb | |||
| b4ab20bbe9 | |||
| f6a95df550 | |||
| f3f660a180 | |||
| 0cf36db010 | |||
| bfeb225bc2 | |||
| 31d486c2e2 | |||
| eda7118df9 | |||
| 7c468052e6 | |||
| 43eca490a7 | |||
| 3710d61a09 | |||
| 5daa5a00f0 | |||
| 6e31993777 | |||
| 1cfad14437 | |||
| 3829ed5880 | |||
| e5cda0e2b1 | |||
| c765bee0cd | |||
| 16942d487a | |||
| 5cb4cfef32 | |||
| 4d86ced1e6 | |||
| ad03431b0a | |||
| 474f1118b6 | |||
| e63d573414 | |||
| 33643ff7fc | |||
| ca25ad57b1 | |||
| fc2cc6ce6b | |||
| fe64dfeb1f | |||
| abf17407ea | |||
| c364383cf0 | |||
| 5033b9ef0d | |||
| 605b255e32 | |||
| d3fdfe4b29 | |||
| c452694348 | |||
| 83fc19e143 | |||
| 6686fe110d | |||
| ed027c23d0 | |||
| 2213bedc12 | |||
| ac7958f335 | |||
| 3301d28615 | |||
| 6033071e61 | |||
| 06775b5623 | |||
| 93a734eecf | |||
| 6f64fea0b1 | |||
| 6b910e11e5 | |||
| b31bb6dd33 | |||
| 104cf504ab | |||
| b462d7627f | |||
| cb4781360a | |||
| 8895e49466 | |||
| d1f4463c2a | |||
| f9b5789cf7 | |||
| 5273fbf57b | |||
| ad6890e7dd | |||
| 46ddc7364c | |||
| a2c0491ae0 | |||
| c686ac8603 | |||
| 268613e1db | |||
| 5ba918d213 | |||
| 21dcb7b13c | |||
| 40ff71b2cd | |||
| b017cc07ce | |||
| fa773c3024 | |||
| a1369aaad0 | |||
| 6516c7aef3 | |||
| c43dfecec6 | |||
| 53e95a7f74 | |||
| 2a81a617e1 | |||
| 37067f17e2 | |||
| f8caa02f10 | |||
| fc2f41096f | |||
| 3eefaac885 | |||
| 0dfe37f998 | |||
| e2439984ae | |||
| 9401e7cb51 | |||
| 4471eb587d | |||
| 3c6037a798 | |||
| d1a0dfbb97 | |||
| 94a542a1d1 | |||
| f5fcfaba0c | |||
| 9c151ca151 | |||
| e667436a98 | |||
| 9aacff8b54 | |||
| 9b43d204e2 | |||
| 0faf801de7 | |||
| dd1cca1a0a | |||
| eca8c28dea | |||
| 333ef9b48a | |||
| 1e98e19aaf | |||
| 308fdcf9cf | |||
| d803b3ae7d | |||
| aa121aa1de | |||
| 8138a26c2d | |||
| 26fa3db66d | |||
| 7779d021b4 | |||
| 6b44143f5b | |||
| 0f207faa3e | |||
| 28b43eff7c | |||
| c257b75a66 | |||
| 893e14cc39 | |||
| 30d5b7fd66 | |||
| 2ef8136308 |
@@ -0,0 +1,57 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignOperands: false
|
||||
AlignTrailingComments: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: true
|
||||
AfterControlStatement: false
|
||||
AfterEnum: true
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
AfterExternBlock: true
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: true
|
||||
BeforeWhile: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBraces: Custom
|
||||
ColumnLimit: 120
|
||||
IncludeCategories:
|
||||
- Regex: '^<.*'
|
||||
Priority: 1
|
||||
- Regex: '^".*'
|
||||
Priority: 2
|
||||
- Regex: '.*'
|
||||
Priority: 3
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentWidth: 4
|
||||
InsertNewlineAtEOF: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 2
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
...
|
||||
@@ -73,6 +73,7 @@ jobs:
|
||||
sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
|
||||
|
||||
xcodebuild -version > CACHE_KEY.txt
|
||||
brew list --versions >> CACHE_KEY.txt
|
||||
echo $MANUAL_CACHING >> CACHE_KEY.txt
|
||||
echo "$GITHUB_WORKSPACE" >> CACHE_KEY.txt
|
||||
if [ "$AUTO_CACHING" = "1" ]; then
|
||||
|
||||
@@ -9,11 +9,38 @@ on:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
noResponse:
|
||||
waiting-for-answer:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lee-dohm/no-response@v0.5.0
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
# Label requiring a response
|
||||
responseRequiredLabel: waiting for answer
|
||||
|
||||
needs-user-action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lee-dohm/no-response@v0.5.0
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
responseRequiredLabel: needs user action
|
||||
|
||||
cant-reproduce:
|
||||
if: github.event_name != 'issue_comment'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: lee-dohm/no-response@v0.5.0
|
||||
with:
|
||||
token: ${{ github.token }}
|
||||
responseRequiredLabel: cant reproduce
|
||||
closeComment: >
|
||||
This issue has been automatically closed because no developer succeeded to
|
||||
reproduce the issue with the given reproduction steps. With only the
|
||||
information that is currently in the issue, we don't have enough
|
||||
information to take action. Please reach out if you find what's missing to
|
||||
reproduce the issue so that we can investigate further.
|
||||
|
||||
|
||||
Note that GitHub is a developer communication platform. If you're an ordinary
|
||||
user seeking for help, get to support crew via `Settings -> Ask question` in
|
||||
the application.
|
||||
|
||||
@@ -25,9 +25,20 @@ And many more. Check out our [Documentation](https://docs.ayugram.one/desktop/).
|
||||
|
||||
### Windows
|
||||
|
||||
#### Official
|
||||
|
||||
You can download prebuilt Windows binary from [Releases tab](https://github.com/AyuGram/AyuGramDesktop/releases) or from
|
||||
the [Telegram topic](https://t.me/ayugramchat/12788).
|
||||
|
||||
#### Scoop
|
||||
|
||||
```bash
|
||||
scoop bucket add extras
|
||||
scoop install ayugram
|
||||
```
|
||||
|
||||
#### Self-built
|
||||
|
||||
Follow [official guide](https://github.com/AyuGram/AyuGramDesktop/blob/dev/docs/building-win-x64.md) if you want to
|
||||
build by yourself.
|
||||
|
||||
@@ -65,8 +76,6 @@ Enjoy using **AyuGram**? Consider sending us a tip!
|
||||
### Libraries used
|
||||
|
||||
- [JSON for Modern C++](https://github.com/nlohmann/json)
|
||||
- [BitConverter](https://github.com/YanjieHe/BitConverter)
|
||||
- [Not Enough Standards](https://github.com/Alairion/not-enough-standards)
|
||||
- [SQLite](https://github.com/sqlite/sqlite)
|
||||
- [sqlite_orm](https://github.com/fnc12/sqlite_orm)
|
||||
|
||||
|
||||
@@ -96,6 +96,8 @@ nice_target_sources(Telegram ${src_loc}
|
||||
PRIVATE
|
||||
${style_files}
|
||||
|
||||
ayu/ayu_worker.cpp
|
||||
ayu/ayu_worker.h
|
||||
ayu/ayu_url_handlers.cpp
|
||||
ayu/ayu_url_handlers.h
|
||||
ayu/ayu_state.cpp
|
||||
@@ -114,10 +116,8 @@ PRIVATE
|
||||
ayu/utils/telegram_helpers.h
|
||||
ayu/utils/windows_utils.cpp
|
||||
ayu/utils/windows_utils.h
|
||||
ayu/ui/ayu_lottie.cpp
|
||||
ayu/ui/ayu_lottie.h
|
||||
ayu/ui/ayu_assets.cpp
|
||||
ayu/ui/ayu_assets.h
|
||||
ayu/ui/ayu_logo.cpp
|
||||
ayu/ui/ayu_logo.h
|
||||
ayu/ui/utils/ayu_profile_values.cpp
|
||||
ayu/ui/utils/ayu_profile_values.h
|
||||
ayu/ui/settings/icon_picker.cpp
|
||||
@@ -134,39 +134,35 @@ PRIVATE
|
||||
ayu/ui/sections/edited/edited_log_item.h
|
||||
ayu/ui/sections/edited/edited_log_section.cpp
|
||||
ayu/ui/sections/edited/edited_log_section.h
|
||||
ayu/ui/boxes/voice_confirmation_box.cpp
|
||||
ayu/ui/boxes/voice_confirmation_box.h
|
||||
ayu/ui/boxes/message_history_box.cpp
|
||||
ayu/ui/boxes/message_history_box.h
|
||||
ayu/ui/boxes/confirmation_box.cpp
|
||||
ayu/ui/boxes/confirmation_box.h
|
||||
ayu/ui/boxes/server_read_confirmation_box.cpp
|
||||
ayu/ui/boxes/server_read_confirmation_box.h
|
||||
ayu/ui/boxes/edit_deleted_mark.cpp
|
||||
ayu/ui/boxes/edit_deleted_mark.h
|
||||
ayu/ui/boxes/edit_edited_mark.cpp
|
||||
ayu/ui/boxes/edit_edited_mark.h
|
||||
ayu/ui/boxes/font_selector.cpp
|
||||
ayu/ui/boxes/font_selector.h
|
||||
ayu/sync/ayu_sync_controller.cpp
|
||||
ayu/sync/ayu_sync_controller.h
|
||||
ayu/sync/models.h
|
||||
ayu/sync/utils/ayu_pipe_wrapper.cpp
|
||||
ayu/sync/utils/ayu_pipe_wrapper.h
|
||||
ayu/sync/utils/process_utils.hpp
|
||||
ayu/messages/ayu_messages_controller.cpp
|
||||
ayu/messages/ayu_messages_controller.h
|
||||
ayu/libs/pipe.hpp
|
||||
ayu/ui/boxes/theme_selector_box.cpp
|
||||
ayu/ui/boxes/theme_selector_box.h
|
||||
ayu/ui/boxes/message_shot_box.cpp
|
||||
ayu/ui/boxes/message_shot_box.h
|
||||
ayu/ui/components/image_view.cpp
|
||||
ayu/ui/components/image_view.h
|
||||
ayu/libs/json.hpp
|
||||
ayu/libs/json_ext.hpp
|
||||
ayu/libs/bit_converter.hpp
|
||||
ayu/libs/sqlite/sqlite3.c
|
||||
ayu/libs/sqlite/sqlite3.h
|
||||
ayu/libs/sqlite/sqlite_orm.h
|
||||
ayu/features/streamer_mode/streamer_mode_windows.cpp
|
||||
ayu/features/streamer_mode/streamer_mode_linux.cpp
|
||||
ayu/features/streamer_mode/streamer_mode.h
|
||||
ayu/database/entities.h
|
||||
ayu/database/ayu_database.cpp
|
||||
ayu/database/ayu_database.h
|
||||
ayu/features/messageshot/message_shot.cpp
|
||||
ayu/features/messageshot/message_shot.h
|
||||
ayu/data/messages_storage.cpp
|
||||
ayu/data/messages_storage.h
|
||||
ayu/data/entities.h
|
||||
ayu/data/ayu_database.cpp
|
||||
ayu/data/ayu_database.h
|
||||
|
||||
api/api_attached_stickers.cpp
|
||||
api/api_attached_stickers.h
|
||||
@@ -478,6 +474,8 @@ PRIVATE
|
||||
chat_helpers/tabbed_section.h
|
||||
chat_helpers/tabbed_selector.cpp
|
||||
chat_helpers/tabbed_selector.h
|
||||
chat_helpers/ttl_media_layer_widget.cpp
|
||||
chat_helpers/ttl_media_layer_widget.h
|
||||
core/application.cpp
|
||||
core/application.h
|
||||
core/base_integration.cpp
|
||||
@@ -585,6 +583,7 @@ PRIVATE
|
||||
data/data_groups.h
|
||||
data/data_histories.cpp
|
||||
data/data_histories.h
|
||||
data/data_lastseen_status.h
|
||||
data/data_location.cpp
|
||||
data/data_location.h
|
||||
data/data_media_rotation.cpp
|
||||
@@ -681,6 +680,8 @@ PRIVATE
|
||||
dialogs/dialogs_row.h
|
||||
dialogs/dialogs_search_from_controllers.cpp
|
||||
dialogs/dialogs_search_from_controllers.h
|
||||
dialogs/dialogs_search_tags.cpp
|
||||
dialogs/dialogs_search_tags.h
|
||||
dialogs/dialogs_widget.cpp
|
||||
dialogs/dialogs_widget.h
|
||||
dialogs/ui/dialogs_layout.cpp
|
||||
@@ -820,6 +821,8 @@ PRIVATE
|
||||
history/view/reactions/history_view_reactions_strip.h
|
||||
history/view/reactions/history_view_reactions_tabs.cpp
|
||||
history/view/reactions/history_view_reactions_tabs.h
|
||||
history/view/history_view_about_view.cpp
|
||||
history/view/history_view_about_view.h
|
||||
history/view/history_view_bottom_info.cpp
|
||||
history/view/history_view_bottom_info.h
|
||||
history/view/history_view_contact_status.cpp
|
||||
@@ -1084,6 +1087,7 @@ PRIVATE
|
||||
media/audio/media_audio.h
|
||||
media/audio/media_audio_capture.cpp
|
||||
media/audio/media_audio_capture.h
|
||||
media/audio/media_audio_capture_common.h
|
||||
media/audio/media_audio_ffmpeg_loader.cpp
|
||||
media/audio/media_audio_ffmpeg_loader.h
|
||||
media/audio/media_audio_loader.cpp
|
||||
@@ -1299,8 +1303,6 @@ PRIVATE
|
||||
platform/mac/touchbar/mac_touchbar_manager.mm
|
||||
platform/mac/touchbar/mac_touchbar_media_view.h
|
||||
platform/mac/touchbar/mac_touchbar_media_view.mm
|
||||
platform/win/audio_win.cpp
|
||||
platform/win/audio_win.h
|
||||
platform/win/file_utilities_win.cpp
|
||||
platform/win/file_utilities_win.h
|
||||
platform/win/launcher_win.cpp
|
||||
@@ -1324,7 +1326,6 @@ PRIVATE
|
||||
platform/win/windows_autostart_task.h
|
||||
platform/win/windows_toast_activator.cpp
|
||||
platform/win/windows_toast_activator.h
|
||||
platform/platform_audio.h
|
||||
platform/platform_file_utilities.h
|
||||
platform/platform_launcher.h
|
||||
platform/platform_integration.cpp
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg baseProfile="tiny" version="1.2" viewBox="0 0 72 72" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m27.57 30.79q0.77-0.44 1.14-1.28 4.38-9.86 4.67-24.25 0.03-1.64 1.63-1.54 1.14 0.07 1.9 0.65c14.45 10.9 28.35 31.97 18.06 50.37-9.55 17.08-32.38 15.75-41.59-0.69-5.25-9.37-0.83-23.06 4.26-32.03a2.13 2.12 43.5 0 1 3.64-0.09l5.53 8.68a0.57 0.56-31.3 0 0 0.76 0.18z" fill="#fff"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 283 B |
|
After Width: | Height: | Size: 407 B |
|
After Width: | Height: | Size: 475 B |
|
After Width: | Height: | Size: 648 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 544 B |
|
After Width: | Height: | Size: 901 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 776 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 561 B |
|
After Width: | Height: | Size: 1011 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 267 B |
|
After Width: | Height: | Size: 368 B |
|
After Width: | Height: | Size: 283 B |
|
After Width: | Height: | Size: 378 B |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 634 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 692 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 614 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 593 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 496 B |
|
After Width: | Height: | Size: 895 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 269 B |
|
After Width: | Height: | Size: 389 B |
|
After Width: | Height: | Size: 529 B |
|
After Width: | Height: | Size: 528 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 614 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
@@ -141,6 +141,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_status_last_week" = "last seen within a week";
|
||||
"lng_status_last_month" = "last seen within a month";
|
||||
"lng_status_lastseen_now" = "last seen just now";
|
||||
"lng_status_lastseen_when" = "when?";
|
||||
"lng_status_lastseen_minutes#one" = "last seen {count} minute ago";
|
||||
"lng_status_lastseen_minutes#other" = "last seen {count} minutes ago";
|
||||
"lng_status_lastseen_hours#one" = "last seen {count} hour ago";
|
||||
@@ -168,6 +169,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_remember" = "Remember this choice";
|
||||
|
||||
"lng_lastseen_show_title" = "Show Your Last Seen";
|
||||
"lng_lastseen_show_about" = "To see **{user}'s** Last Seen time, either start\nshowing your own Last Seen time...";
|
||||
"lng_lastseen_show_button" = "Show My Last Seen";
|
||||
"lng_lastseen_or" = "or";
|
||||
"lng_lastseen_premium_title" = "Upgrade to Premium";
|
||||
"lng_lastseen_premium_about" = "Subscription will let you see **{user}'s** Last Seen\nstatus without showing yours.";
|
||||
"lng_lastseen_premium_button" = "Subscribe to Telegram Premium";
|
||||
"lng_lastseen_shown_toast" = "Your last seen time is now visible.";
|
||||
|
||||
"lng_readtime_show_title" = "Show Your Read Date";
|
||||
"lng_readtime_show_about" = "To see when **{user}** read the message,\neither start showing your own read time...";
|
||||
"lng_readtime_show_button" = "Show My Read Time";
|
||||
"lng_readtime_or" = "or";
|
||||
"lng_readtime_premium_title" = "Upgrade to Premium";
|
||||
"lng_readtime_premium_about" = "Subscription will let you see **{user}'s** read time\nwithout showing yours.";
|
||||
"lng_readtime_premium_button" = "Subscribe to Telegram Premium";
|
||||
"lng_readtime_shown_toast" = "Your read times are now visible.";
|
||||
|
||||
"lng_channels_limit_title" = "Too Many Communities";
|
||||
"lng_channels_limit1#one" = "You are a member of **{count}** groups and channels.";
|
||||
"lng_channels_limit1#other" = "You are a member of **{count}** groups and channels.";
|
||||
@@ -414,7 +433,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_username_invalid" = "This username is invalid.";
|
||||
"lng_username_occupied" = "This username is already occupied.";
|
||||
"lng_username_too_short" = "This username is too short.";
|
||||
"lng_username_purchase_available" = "Sorry, this link is occupied by someone. But it's available for purchase through\nofficial {link}.";
|
||||
"lng_username_purchase_available" = "**This username is already taken.** However, it is currently available for purchase. {link}";
|
||||
"lng_username_purchase_available_link" = "Learn more...";
|
||||
"lng_username_bad_symbols" = "Only a-z, 0-9, and underscores allowed.";
|
||||
"lng_username_available" = "This username is available.";
|
||||
"lng_username_not_found" = "User @{user} not found.";
|
||||
@@ -611,6 +631,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_call_accept_calls" = "Accept calls from this device";
|
||||
"lng_settings_call_device_default" = "Same as the System";
|
||||
|
||||
"lng_settings_section_devices" = "Speakers and Camera";
|
||||
"lng_settings_devices_calls" = "Calls and video chats";
|
||||
"lng_settings_devices_calls_same" = "Use the same devices for calls";
|
||||
"lng_settings_devices_inactive" = "Unavailable";
|
||||
|
||||
"lng_settings_language" = "Language";
|
||||
"lng_settings_default_scale" = "Default interface scale";
|
||||
"lng_settings_connection_type" = "Connection type";
|
||||
@@ -624,6 +649,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_settings_phone_number_privacy" = "Phone number";
|
||||
"lng_settings_forwards_privacy" = "Forwarded messages";
|
||||
"lng_settings_profile_photo_privacy" = "Profile photo";
|
||||
"lng_settings_messages_privacy" = "Messages";
|
||||
"lng_settings_voices_privacy" = "Voice messages";
|
||||
"lng_settings_bio_privacy" = "Bio";
|
||||
"lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages.";
|
||||
@@ -1060,6 +1086,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_contacts" = "My contacts";
|
||||
"lng_edit_privacy_close_friends" = "Close friends";
|
||||
"lng_edit_privacy_nobody" = "Nobody";
|
||||
"lng_edit_privacy_premium" = "Premium users";
|
||||
"lng_edit_privacy_exceptions" = "Add exceptions";
|
||||
|
||||
"lng_edit_privacy_exceptions_count#one" = "{count} user";
|
||||
@@ -1086,6 +1113,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_lastseen_exceptions" = "These settings will override the values above.";
|
||||
"lng_edit_privacy_lastseen_always_title" = "Always share with";
|
||||
"lng_edit_privacy_lastseen_never_title" = "Never share with";
|
||||
"lng_edit_lastseen_hide_read_time" = "Hide read time";
|
||||
"lng_edit_lastseen_hide_read_time_about" = "Hide the time when you read messages from people who can't see your last seen. If you turn this on, their read time will also be hidden from you. This setting does not affect group chats.";
|
||||
"lng_edit_lastseen_subscribe" = "Subscribe to Telegram Premium";
|
||||
"lng_edit_lastseen_subscribe_about" = "If you subscribe to Premium, you will see other users' last seen and read time even if you hid yours from them (unless they specifically restricted it).";
|
||||
|
||||
"lng_edit_privacy_groups_title" = "Group invite settings";
|
||||
"lng_edit_privacy_groups_header" = "Who can invite you to groups and channels";
|
||||
@@ -1157,6 +1188,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_edit_privacy_voices_always_title" = "Always allow";
|
||||
"lng_edit_privacy_voices_never_title" = "Never allow";
|
||||
|
||||
"lng_messages_privacy_title" = "Messages";
|
||||
"lng_messages_privacy_subtitle" = "Who can send me messages?";
|
||||
"lng_messages_privacy_everyone" = "Everybody";
|
||||
"lng_messages_privacy_restricted" = "My Contacts and Premium Users";
|
||||
"lng_messages_privacy_about" = "You can restrict incoming messages to only contacts and Premium users.";
|
||||
"lng_messages_privacy_premium_button" = "Subscribe to Telegram Premium";
|
||||
"lng_messages_privacy_premium_about" = "Subscribe now to change this setting and get access to other exclusive features of Telegram Premium.";
|
||||
"lng_messages_privacy_premium" = "Only subscribers of {link} can select this option.";
|
||||
"lng_messages_privacy_premium_link" = "Telegram Premium";
|
||||
|
||||
"lng_self_destruct_title" = "Account self-destruction";
|
||||
"lng_self_destruct_description" = "If you don't come online at least once within this period, your account will be deleted along with all groups, messages and contacts.";
|
||||
"lng_self_destruct_sessions_title" = "Session termination";
|
||||
@@ -1464,6 +1505,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_report_messages_none" = "Select Messages";
|
||||
"lng_report_messages_count#one" = "Report {count} Message";
|
||||
"lng_report_messages_count#other" = "Report {count} Messages";
|
||||
"lng_report_reaction" = "Report reaction";
|
||||
"lng_report_and_ban" = "Ban and report";
|
||||
"lng_report_reaction_title" = "Report reaction";
|
||||
"lng_report_reaction_about" = "Are you sure you want to report reactions from this user?";
|
||||
"lng_report_and_ban_button" = "Ban user";
|
||||
"lng_report_details_about" = "Please enter any additional details relevant to your report.";
|
||||
"lng_report_details" = "Additional Details";
|
||||
"lng_report_reason_spam" = "Spam";
|
||||
@@ -1719,6 +1765,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_ttl_voice_expired" = "Voice message expired";
|
||||
"lng_ttl_round_sent" = "You sent a self-destructing video message.";
|
||||
"lng_ttl_round_expired" = "Round message expired";
|
||||
"lng_ttl_voice_tooltip_in" = "This voice message can only be played once.";
|
||||
"lng_ttl_voice_tooltip_out" = "This message will disappear once **{user}** plays it once.";
|
||||
"lng_ttl_voice_close_in" = "Delete and close";
|
||||
"lng_ttl_round_tooltip_in" = "This video message can only be played once.";
|
||||
"lng_ttl_round_tooltip_out" = "This message will disappear once **{user}** plays it once.";
|
||||
|
||||
"lng_profile_add_more_after_create" = "You will be able to add more members after you create the group.";
|
||||
"lng_profile_camera_title" = "Capture yourself";
|
||||
@@ -1980,6 +2031,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_premium_summary_about_emoji_status" = "Add any of thousands emoji next to your name to display current activity.";
|
||||
"lng_premium_summary_subtitle_infinite_reactions" = "Infinite Reactions";
|
||||
"lng_premium_summary_about_infinite_reactions" = "React with thousands of emoji — with multiple reactions per message.";
|
||||
"lng_premium_summary_subtitle_tags_for_messages" = "Tags for Messages";
|
||||
"lng_premium_summary_about_tags_for_messages" = "Organize your Saved Messages with tags for quicker access.";
|
||||
"lng_premium_summary_subtitle_premium_stickers" = "Premium Stickers";
|
||||
"lng_premium_summary_about_premium_stickers" = "Exclusive enlarged stickers featuring additional effects, updated monthly.";
|
||||
"lng_premium_summary_subtitle_animated_emoji" = "Animated Emoji";
|
||||
@@ -2400,6 +2453,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_in_dlg_contact" = "Contact";
|
||||
"lng_in_dlg_audio" = "Voice message";
|
||||
"lng_in_dlg_video_message" = "Video message";
|
||||
"lng_in_dlg_voice_message_ttl" = "One-time Voice Message";
|
||||
"lng_in_dlg_video_message_ttl" = "One-time Video Message";
|
||||
"lng_in_dlg_file" = "File";
|
||||
"lng_in_dlg_sticker" = "Sticker";
|
||||
"lng_in_dlg_sticker_emoji" = "{emoji} Sticker";
|
||||
@@ -2487,6 +2542,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_record_listen_cancel_sure" = "Are you sure you want to discard your recorded voice message?";
|
||||
"lng_record_lock_discard" = "Discard";
|
||||
"lng_record_hold_tip" = "Please hold the mouse button pressed to record a voice message.";
|
||||
"lng_record_once_first_tooltip" = "Click to set this message to **Play Once**.";
|
||||
"lng_record_once_active_tooltip" = "The recipients will be able to listen to it only once.";
|
||||
"lng_will_be_notified" = "Members will be notified when you post";
|
||||
"lng_wont_be_notified" = "Members will not be notified when you post";
|
||||
"lng_willbe_history" = "Please select a chat to start messaging";
|
||||
@@ -2749,17 +2806,34 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_context_seen_reacted#other" = "{count} Reacted";
|
||||
"lng_context_seen_reacted_none" = "Nobody Reacted";
|
||||
"lng_context_seen_reacted_all" = "Show All Reactions";
|
||||
"lng_context_set_as_quick" = "Set As Quick";
|
||||
"lng_context_set_as_quick" = "Set as Quick";
|
||||
"lng_context_filter_by_tag" = "Filter by Tag";
|
||||
"lng_context_tag_add_name" = "Add Name";
|
||||
"lng_context_tag_edit_name" = "Edit Name";
|
||||
"lng_context_remove_tag" = "Remove Tag";
|
||||
"lng_context_delete_from_disk" = "Delete from disk";
|
||||
"lng_context_delete_all_files" = "Delete all files";
|
||||
"lng_context_save_custom_sound" = "Save for notifications";
|
||||
"lng_context_translate" = "Translate";
|
||||
"lng_context_translate_selected" = "Translate Selected Text";
|
||||
"lng_context_read_hidden" = "read";
|
||||
"lng_context_read_show" = "show when";
|
||||
|
||||
"lng_add_tag_about" = "Tag this message with an emoji for quick search.";
|
||||
"lng_subscribe_tag_about" = "Organize your Saved Messages with tags. {link}";
|
||||
"lng_subscribe_tag_link" = "Learn More...";
|
||||
"lng_edit_tag_about" = "You can label your emoji tag with a text name.";
|
||||
"lng_edit_tag_name" = "Name";
|
||||
"lng_add_tag_button" = "Add tags";
|
||||
"lng_add_tag_phrase" = "to messages {arrow}";
|
||||
"lng_add_tag_phrase_long" = "to your Saved Messages {arrow}";
|
||||
"lng_unlock_tags" = "Unlock";
|
||||
|
||||
"lng_context_animated_emoji" = "This message contains emoji from **{name} pack**.";
|
||||
"lng_context_animated_emoji_many#one" = "This message contains emoji from **{count} pack**.";
|
||||
"lng_context_animated_emoji_many#other" = "This message contains emoji from **{count} packs**.";
|
||||
"lng_context_animated_reaction" = "This reaction is from **{name} pack**.";
|
||||
"lng_context_animated_tag" = "This tag is from **{name} pack**.";
|
||||
"lng_context_animated_reactions" = "Reactions contain emoji from **{name} pack**.";
|
||||
"lng_context_animated_reactions_many#one" = "Reactions contain emoji from **{count} pack**.";
|
||||
"lng_context_animated_reactions_many#other" = "Reactions contain emoji from **{count} packs**.";
|
||||
@@ -3397,7 +3471,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_group_call_rtmp_key_copy" = "Copy Stream Key";
|
||||
"lng_group_call_rtmp_key_copied" = "Stream Key copied to clipboard.";
|
||||
"lng_group_call_rtmp_key_warning" = "**Never share your Stream Key with anyone or show it on stream!**";
|
||||
"lng_group_call_rtmp_info" = "To stream video with another app, enter these Server URL and Stream Key in your streaming app.\n\nOnce you start broadcasting in your streaming app, tap Start Streaming below.";
|
||||
"lng_group_call_rtmp_info" = "To stream video with another app, enter these Server URL and Stream Key in your streaming app.\n\nOnce you start broadcasting in your streaming app, click Start Streaming below.";
|
||||
"lng_group_call_rtmp_start" = "Start Streaming";
|
||||
"lng_group_call_rtmp_revoke" = "Revoke Stream Key";
|
||||
"lng_group_call_rtmp_revoke_sure" = "Are you sure you want to revoke your Server URL and Stream Key?";
|
||||
@@ -3565,6 +3639,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_restricted_send_voice_messages" = "{user} restricted sending of voice messages to them.";
|
||||
"lng_restricted_send_video_messages" = "{user} restricted sending of video messages to them.";
|
||||
"lng_restricted_send_non_premium" = "Only Premium users can message {user}.";
|
||||
"lng_restricted_send_non_premium_more" = "Learn more...";
|
||||
|
||||
"lng_send_non_premium_text" = "Subscribe to **Premium**\n to message {user}.";
|
||||
"lng_send_non_premium_go" = "Get Premium";
|
||||
"lng_send_non_premium_story" = "Replies restricted";
|
||||
"lng_send_non_premium_unlock" = "unlock";
|
||||
"lng_send_non_premium_message_toast" = "**{user}** only accepts messages from contacts and {link} subscribers.";
|
||||
"lng_send_non_premium_message_toast_link" = "Telegram Premium";
|
||||
|
||||
"lng_exceptions_list_title" = "Exceptions";
|
||||
"lng_removed_list_title" = "Removed users";
|
||||
@@ -4232,7 +4315,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
"lng_sponsored_hide_ads" = "Hide";
|
||||
"lng_sponsored_title" = "What are sponsored messages?";
|
||||
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you tapped on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description1" = "Unlike other apps, Telegram never uses your private data to target ads. Sponsored messages on Telegram are based solely on the topic of the public channels in which they are shown. This means that no user data is mined or analyzed to display ads, and every user viewing a channel on Telegram sees the same sponsored messages.\n\nUnlike other apps, Telegram doesn't track whether you clicked on a sponsored message and doesn't profile you based on your activity. We also prevent external links in sponsored messages to ensure that third parties can’t spy on our users. We believe that everyone has the right to privacy, and technological platforms should respect that.\n\nTelegram offers a free and unlimited service to hundreds of millions of users, which involves significant server and traffic costs. In order to remain independent and stay true to its values, Telegram developed a paid tool to promote messages with user privacy in mind. We welcome responsible advertisers at:";
|
||||
"lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together.";
|
||||
"lng_sponsored_info_menu" = "About this ad";
|
||||
"lng_sponsored_info_submenu" = "Advertiser: {text}";
|
||||
@@ -4346,6 +4429,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stories_views#one" = "{count} view";
|
||||
"lng_stories_views#other" = "{count} views";
|
||||
"lng_stories_no_views" = "No views";
|
||||
"lng_stories_view_reactions" = "View reactions";
|
||||
"lng_stories_unsupported" = "This story is not supported\nby your version of Telegram.";
|
||||
"lng_stories_cant_reply" = "You can't reply to this story.";
|
||||
"lng_stories_about_silent" = "This video has no sound.";
|
||||
@@ -4400,6 +4484,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_stories_channel_archive_done_many#one" = "{count} story is hidden from the channel page.";
|
||||
"lng_stories_channel_archive_done_many#other" = "{count} stories are hidden from the channel page.";
|
||||
"lng_stories_save_promo" = "Subscribe to {link} to download other people's unprotected stories to disk.";
|
||||
"lng_stories_reaction_as_message" = "Send reaction as a private message";
|
||||
|
||||
"lng_stealth_mode_menu_item" = "Stealth Mode";
|
||||
"lng_stealth_mode_title" = "Stealth Mode";
|
||||
@@ -4648,6 +4733,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_ShowKllButtonInDrawer" = "Show kill app button";
|
||||
"ayu_SettingsShowID" = "Show peer ID";
|
||||
"ayu_SettingsShowID_Hide" = "Hide";
|
||||
"ayu_SettingsShowMessageShot" = "Show message shot";
|
||||
"ayu_SettingsRecentStickersCount" = "Recent stickers count";
|
||||
"ayu_SettingsCustomizationHint" = "After making changes to the \"Customization\" section, you must restart the application.";
|
||||
"ayu_SettingsContextMenuTitle" = "Select item show type";
|
||||
@@ -4661,7 +4747,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_DrawerElementsHeader" = "Drawer Elements";
|
||||
"ayu_TrayElementsHeader" = "Tray Elements";
|
||||
"ayu_RegexFilters" = "Message Filters";
|
||||
"ayu_RegexFiltersAmount" = "filters";
|
||||
"ayu_RegexFiltersAmount_zero" = "%1$d filters";
|
||||
"ayu_RegexFiltersAmount_one" = "1 filter";
|
||||
"ayu_RegexFiltersAmount_two" = "%1$d filters";
|
||||
"ayu_RegexFiltersAmount_few" = "%1$d filters";
|
||||
"ayu_RegexFiltersAmount_many" = "%1$d filters";
|
||||
"ayu_RegexFiltersAmount_other" = "%1$d filters";
|
||||
"ayu_RegexFiltersExcludedAmount_zero" = "%1$d excluded";
|
||||
"ayu_RegexFiltersExcludedAmount_one" = "1 excluded";
|
||||
"ayu_RegexFiltersExcludedAmount_two" = "%1$d excluded";
|
||||
"ayu_RegexFiltersExcludedAmount_few" = "%1$d excluded";
|
||||
"ayu_RegexFiltersExcludedAmount_many" = "%1$d excluded";
|
||||
"ayu_RegexFiltersExcludedAmount_other" = "%1$d excluded";
|
||||
"ayu_RegexFiltersHeader" = "Filters";
|
||||
"ayu_RegexFiltersShared" = "Shared filters";
|
||||
"ayu_RegexFiltersExcluded" = "Excluded filters";
|
||||
@@ -4749,6 +4846,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_EditsHistoryMenuText" = "History";
|
||||
"ayu_PeekOnlineMenuText" = "Peek Online";
|
||||
"ayu_ClearDeletedMenuText" = "Clear Deleted";
|
||||
"ayu_ViewDeletedMenuText" = "View Deleted";
|
||||
"ayu_ViewFiltersMenuText" = "View Filters";
|
||||
"ayu_PeekOnlineSuccess" = "Peeked via";
|
||||
"ayu_OneViewTTL" = "one view";
|
||||
@@ -4778,10 +4876,27 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_MonospaceFont" = "Monospace font";
|
||||
"ayu_FontDefault" = "Default";
|
||||
"ayu_CustomizeFontTitle" = "Customize font";
|
||||
"ayu_MessageShotTopBarText" = "Shot";
|
||||
"ayu_MessageShotPreview" = "Preview";
|
||||
"ayu_MessageShotPreferences" = "Preferences";
|
||||
"ayu_MessageShotCopy" = "Copy";
|
||||
"ayu_MessageShotSave" = "Save";
|
||||
"ayu_MessageShotTheme" = "Theme";
|
||||
"ayu_MessageShotThemeDefault" = "Default";
|
||||
"ayu_MessageShotThemeSelectTitle" = "Select message theme";
|
||||
"ayu_MessageShotThemeApply" = "Apply";
|
||||
"ayu_MessageShotShowBackground" = "Show background";
|
||||
"ayu_MessageShotShowDate" = "Show date";
|
||||
"ayu_MessageShotShowReactions" = "Show reactions";
|
||||
"ayu_MessageShotShowColorfulReplies" = "Show colorful replies";
|
||||
"ayu_SendAsSticker" = "Send as Sticker";
|
||||
"ayu_AyuForwardStatusForwarding" = "Forwarding messages…";
|
||||
"ayu_AyuForwardStatusLoadingMedia" = "Loading media…";
|
||||
"ayu_AyuForwardForwardingDescription" = "Please keep this window open while AyuGram is forwarding your messages.";
|
||||
"ayu_AyuForwardLoadingMediaDescription" = "Please keep this window open while AyuGram is downloading media from your messages.";
|
||||
"ayu_ExpireMediaContextMenuText" = "Burn";
|
||||
"ayu_ExpiringVoiceMessageNote" = "This voice message can be played as many times as you want.";
|
||||
"ayu_ExpiringVideoMessageNote" = "This video message can be played as many times as you want.";
|
||||
"ayu_UserNotFoundMessage" = "User not found.";
|
||||
"ayu_DeleteDateMenuText" = "Delete Date";
|
||||
"ayu_ReadDateMenuText" = "Read Date";
|
||||
@@ -4795,6 +4910,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"ayu_IDCopiedToast" = "ID copied to clipboard.";
|
||||
"ayu_ContextHideMessage" = "Hide";
|
||||
"ayu_ContextCopyCallbackData" = "Copy Callback Data";
|
||||
"ayu_RegisterURLScheme" = "Register URL scheme";
|
||||
"ayu_LocalPremiumNotice" = "You're using **local** Telegram Premium.\nIt **won't** give you any benefits, except translator.\n**Enjoy the star near your nickname!**";
|
||||
"ayu_SettingsWatermark" = "AyuGram developed and maintained by Radolyn Labs.";
|
||||
"ayu_ConfirmationSticker" = "Do you want to send this sticker?";
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<file alias="recording/info_audio.svg">../../art/recording/recording_info_audio.svg</file>
|
||||
<file alias="recording/info_video_landscape.svg">../../art/recording/recording_info_video_landscape.svg</file>
|
||||
<file alias="recording/info_video_portrait.svg">../../art/recording/recording_info_video_portrait.svg</file>
|
||||
<file alias="ttl/video_message_icon.svg">../../art/ttl/video_message_icon.svg</file>
|
||||
<file alias="icons/settings/dino.svg">../../icons/settings/dino.svg</file>
|
||||
<file alias="icons/settings/star.svg">../../icons/settings/star.svg</file>
|
||||
<file alias="icons/settings/starmini.svg">../../icons/settings/starmini.svg</file>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="4.14.3.0" />
|
||||
Version="4.14.13.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,14,3,0
|
||||
PRODUCTVERSION 4,14,3,0
|
||||
FILEVERSION 4,14,13,0
|
||||
PRODUCTVERSION 4,14,13,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -62,10 +62,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop"
|
||||
VALUE "FileVersion", "4.14.3.0"
|
||||
VALUE "FileVersion", "4.14.13.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "4.14.3.0"
|
||||
VALUE "ProductVersion", "4.14.13.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 4,14,3,0
|
||||
PRODUCTVERSION 4,14,3,0
|
||||
FILEVERSION 4,14,13,0
|
||||
PRODUCTVERSION 4,14,13,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -53,10 +53,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Radolyn Labs"
|
||||
VALUE "FileDescription", "AyuGram Desktop Updater"
|
||||
VALUE "FileVersion", "4.14.3.0"
|
||||
VALUE "FileVersion", "4.14.13.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2024"
|
||||
VALUE "ProductName", "AyuGram Desktop"
|
||||
VALUE "ProductVersion", "4.14.3.0"
|
||||
VALUE "ProductVersion", "4.14.13.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -84,18 +84,61 @@ void GlobalPrivacy::dismissArchiveAndMuteSuggestion() {
|
||||
u"AUTOARCHIVE_POPULAR"_q);
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateHideReadTime(bool hide) {
|
||||
update(
|
||||
archiveAndMuteCurrent(),
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hide,
|
||||
newRequirePremiumCurrent());
|
||||
}
|
||||
|
||||
bool GlobalPrivacy::hideReadTimeCurrent() const {
|
||||
return _hideReadTime.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GlobalPrivacy::hideReadTime() const {
|
||||
return _hideReadTime.value();
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateNewRequirePremium(bool value) {
|
||||
update(
|
||||
archiveAndMuteCurrent(),
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hideReadTimeCurrent(),
|
||||
value);
|
||||
}
|
||||
|
||||
bool GlobalPrivacy::newRequirePremiumCurrent() const {
|
||||
return _newRequirePremium.current();
|
||||
}
|
||||
|
||||
rpl::producer<bool> GlobalPrivacy::newRequirePremium() const {
|
||||
return _newRequirePremium.value();
|
||||
}
|
||||
|
||||
|
||||
void GlobalPrivacy::updateArchiveAndMute(bool value) {
|
||||
update(value, unarchiveOnNewMessageCurrent());
|
||||
update(
|
||||
value,
|
||||
unarchiveOnNewMessageCurrent(),
|
||||
hideReadTimeCurrent(),
|
||||
newRequirePremiumCurrent());
|
||||
}
|
||||
|
||||
void GlobalPrivacy::updateUnarchiveOnNewMessage(
|
||||
UnarchiveOnNewMessage value) {
|
||||
update(archiveAndMuteCurrent(), value);
|
||||
update(
|
||||
archiveAndMuteCurrent(),
|
||||
value,
|
||||
hideReadTimeCurrent(),
|
||||
newRequirePremiumCurrent());
|
||||
}
|
||||
|
||||
void GlobalPrivacy::update(
|
||||
bool archiveAndMute,
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage) {
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage,
|
||||
bool hideReadTime,
|
||||
bool newRequirePremium) {
|
||||
using Flag = MTPDglobalPrivacySettings::Flag;
|
||||
|
||||
_api.request(_requestId).cancel();
|
||||
@@ -108,17 +151,26 @@ void GlobalPrivacy::update(
|
||||
: Flag())
|
||||
| (unarchiveOnNewMessage != UnarchiveOnNewMessage::AnyUnmuted
|
||||
? Flag::f_keep_archived_folders
|
||||
: Flag())
|
||||
| (hideReadTime ? Flag::f_hide_read_marks : Flag())
|
||||
| ((newRequirePremium && _session->premium())
|
||||
? Flag::f_new_noncontact_peers_require_premium
|
||||
: Flag());
|
||||
_requestId = _api.request(MTPaccount_SetGlobalPrivacySettings(
|
||||
MTP_globalPrivacySettings(MTP_flags(flags))
|
||||
)).done([=](const MTPGlobalPrivacySettings &result) {
|
||||
_requestId = 0;
|
||||
apply(result);
|
||||
}).fail([=] {
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
if (error.type() == u"PREMIUM_ACCOUNT_REQUIRED"_q) {
|
||||
update(archiveAndMute, unarchiveOnNewMessage, hideReadTime, {});
|
||||
}
|
||||
}).send();
|
||||
_archiveAndMute = archiveAndMute;
|
||||
_unarchiveOnNewMessage = unarchiveOnNewMessage;
|
||||
_hideReadTime = hideReadTime;
|
||||
_newRequirePremium = newRequirePremium;
|
||||
}
|
||||
|
||||
void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
|
||||
@@ -129,6 +181,8 @@ void GlobalPrivacy::apply(const MTPGlobalPrivacySettings &data) {
|
||||
: data.is_keep_archived_folders()
|
||||
? UnarchiveOnNewMessage::NotInFoldersUnmuted
|
||||
: UnarchiveOnNewMessage::AnyUnmuted;
|
||||
_hideReadTime = data.is_hide_read_marks();
|
||||
_newRequirePremium = data.is_new_noncontact_peers_require_premium();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -41,12 +41,22 @@ public:
|
||||
[[nodiscard]] rpl::producer<> suggestArchiveAndMute() const;
|
||||
void dismissArchiveAndMuteSuggestion();
|
||||
|
||||
void updateHideReadTime(bool hide);
|
||||
[[nodiscard]] bool hideReadTimeCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> hideReadTime() const;
|
||||
|
||||
void updateNewRequirePremium(bool value);
|
||||
[[nodiscard]] bool newRequirePremiumCurrent() const;
|
||||
[[nodiscard]] rpl::producer<bool> newRequirePremium() const;
|
||||
|
||||
private:
|
||||
void apply(const MTPGlobalPrivacySettings &data);
|
||||
|
||||
void update(
|
||||
bool archiveAndMute,
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage);
|
||||
UnarchiveOnNewMessage unarchiveOnNewMessage,
|
||||
bool hideReadTime,
|
||||
bool newRequirePremium);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
@@ -55,6 +65,8 @@ private:
|
||||
rpl::variable<UnarchiveOnNewMessage> _unarchiveOnNewMessage
|
||||
= UnarchiveOnNewMessage::None;
|
||||
rpl::variable<bool> _showArchiveAndMute = false;
|
||||
rpl::variable<bool> _hideReadTime = false;
|
||||
rpl::variable<bool> _newRequirePremium = false;
|
||||
std::vector<Fn<void()>> _callbacks;
|
||||
|
||||
};
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_message_reaction_id.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
@@ -43,6 +44,23 @@ constexpr auto kSearchPerPage = 50;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] QString RequestToToken(
|
||||
const MessagesSearch::Request &request) {
|
||||
auto result = request.query;
|
||||
if (request.from) {
|
||||
result += '\n' + QString::number(request.from->id.value);
|
||||
}
|
||||
for (const auto &tag : request.tags) {
|
||||
result += '\n';
|
||||
if (const auto customId = tag.custom()) {
|
||||
result += u"custom"_q + QString::number(customId);
|
||||
} else {
|
||||
result += u"emoji"_q + tag.emoji();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MessagesSearch::MessagesSearch(not_null<History*> history)
|
||||
@@ -54,9 +72,8 @@ MessagesSearch::~MessagesSearch() {
|
||||
base::take(_searchInHistoryRequest));
|
||||
}
|
||||
|
||||
void MessagesSearch::searchMessages(const QString &query, PeerData *from) {
|
||||
_query = query;
|
||||
_from = from;
|
||||
void MessagesSearch::searchMessages(Request request) {
|
||||
_request = std::move(request);
|
||||
_offsetId = {};
|
||||
searchRequest();
|
||||
}
|
||||
@@ -69,8 +86,7 @@ void MessagesSearch::searchMore() {
|
||||
}
|
||||
|
||||
void MessagesSearch::searchRequest() {
|
||||
const auto nextToken = _query
|
||||
+ QString::number(_from ? _from->id.value : 0);
|
||||
const auto nextToken = RequestToToken(_request);
|
||||
if (!_offsetId) {
|
||||
const auto it = _cacheOfStartByToken.find(nextToken);
|
||||
if (it != end(_cacheOfStartByToken)) {
|
||||
@@ -80,17 +96,21 @@ void MessagesSearch::searchRequest() {
|
||||
}
|
||||
}
|
||||
auto callback = [=](Fn<void()> finish) {
|
||||
const auto flags = _from
|
||||
? MTP_flags(MTPmessages_Search::Flag::f_from_id)
|
||||
: MTP_flags(0);
|
||||
using Flag = MTPmessages_Search::Flag;
|
||||
const auto from = _request.from;
|
||||
const auto fromPeer = _history->peer->isUser() ? nullptr : from;
|
||||
const auto savedPeer = _history->peer->isSelf() ? from : nullptr;
|
||||
_requestId = _history->session().api().request(MTPmessages_Search(
|
||||
flags,
|
||||
MTP_flags((fromPeer ? Flag::f_from_id : Flag())
|
||||
| (savedPeer ? Flag::f_saved_peer_id : Flag())
|
||||
| (_request.tags.empty() ? Flag() : Flag::f_saved_reaction)),
|
||||
_history->peer->input,
|
||||
MTP_string(_query),
|
||||
(_from
|
||||
? _from->input
|
||||
: MTP_inputPeerEmpty()),
|
||||
MTPInputPeer(), // saved_peer_id
|
||||
MTP_string(_request.query),
|
||||
(fromPeer ? fromPeer->input : MTP_inputPeerEmpty()),
|
||||
(savedPeer ? savedPeer->input : MTP_inputPeerEmpty()),
|
||||
MTP_vector_from_range(_request.tags | ranges::views::transform(
|
||||
Data::ReactionToMTP
|
||||
)),
|
||||
MTPint(), // top_msg_id
|
||||
MTP_inputMessagesFilterEmpty(),
|
||||
MTP_int(0), // min_date
|
||||
|
||||
@@ -7,10 +7,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/qt/qt_compare.h"
|
||||
#include "data/data_message_reaction_id.h"
|
||||
|
||||
class HistoryItem;
|
||||
class History;
|
||||
class PeerData;
|
||||
|
||||
namespace Data {
|
||||
struct ReactionId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
struct FoundMessages {
|
||||
@@ -21,10 +28,23 @@ struct FoundMessages {
|
||||
|
||||
class MessagesSearch final {
|
||||
public:
|
||||
struct Request {
|
||||
QString query;
|
||||
PeerData *from = nullptr;
|
||||
std::vector<Data::ReactionId> tags;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Request &,
|
||||
const Request &) = default;
|
||||
friend inline auto operator<=>(
|
||||
const Request &,
|
||||
const Request &) = default;
|
||||
};
|
||||
|
||||
explicit MessagesSearch(not_null<History*> history);
|
||||
~MessagesSearch();
|
||||
|
||||
void searchMessages(const QString &query, PeerData *from);
|
||||
void searchMessages(Request request);
|
||||
void searchMore();
|
||||
|
||||
[[nodiscard]] rpl::producer<FoundMessages> messagesFounds() const;
|
||||
@@ -41,8 +61,7 @@ private:
|
||||
|
||||
base::flat_map<QString, TLMessages> _cacheOfStartByToken;
|
||||
|
||||
QString _query;
|
||||
PeerData *_from = nullptr;
|
||||
Request _request;
|
||||
MsgId _offsetId;
|
||||
|
||||
int _searchInHistoryRequest = 0; // Not real mtpRequestId.
|
||||
|
||||
@@ -11,12 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
namespace Api {
|
||||
|
||||
bool MessagesSearchMerged::RequestCompare::operator()(
|
||||
const Request &a,
|
||||
const Request &b) const {
|
||||
return (a.query < b.query) && (a.from < b.from);
|
||||
}
|
||||
|
||||
MessagesSearchMerged::MessagesSearchMerged(not_null<History*> history)
|
||||
: _apiSearch(history) {
|
||||
if (const auto migrated = history->migrateFrom()) {
|
||||
@@ -88,9 +82,9 @@ void MessagesSearchMerged::clear() {
|
||||
void MessagesSearchMerged::search(const Request &search) {
|
||||
if (_migratedSearch) {
|
||||
_waitingForTotal = true;
|
||||
_migratedSearch->searchMessages(search.query, search.from);
|
||||
_migratedSearch->searchMessages(search);
|
||||
}
|
||||
_apiSearch.searchMessages(search.query, search.from);
|
||||
_apiSearch.searchMessages(search);
|
||||
}
|
||||
|
||||
void MessagesSearchMerged::searchMore() {
|
||||
|
||||
@@ -12,19 +12,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
class History;
|
||||
class PeerData;
|
||||
|
||||
namespace Data {
|
||||
struct ReactionId;
|
||||
} // namespace Data
|
||||
|
||||
namespace Api {
|
||||
|
||||
// Search in both of history and migrated history, if it exists.
|
||||
class MessagesSearchMerged final {
|
||||
public:
|
||||
struct Request {
|
||||
QString query;
|
||||
PeerData *from = nullptr;
|
||||
};
|
||||
struct RequestCompare {
|
||||
bool operator()(const Request &a, const Request &b) const;
|
||||
};
|
||||
using CachedRequests = std::set<Request, RequestCompare>;
|
||||
using Request = MessagesSearch::Request;
|
||||
using CachedRequests = base::flat_set<Request>;
|
||||
|
||||
MessagesSearchMerged(not_null<History*> history);
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -334,6 +338,72 @@ const Data::SubscriptionOptions &Premium::subscriptionOptions() const {
|
||||
return _subscriptionOptions;
|
||||
}
|
||||
|
||||
rpl::producer<> Premium::somePremiumRequiredResolved() const {
|
||||
return _somePremiumRequiredResolved.events();
|
||||
}
|
||||
|
||||
void Premium::resolvePremiumRequired(not_null<UserData*> user) {
|
||||
_resolvePremiumRequiredUsers.emplace(user);
|
||||
if (!_premiumRequiredRequestScheduled
|
||||
&& _resolvePremiumRequestedUsers.empty()) {
|
||||
_premiumRequiredRequestScheduled = true;
|
||||
crl::on_main(_session, [=] {
|
||||
requestPremiumRequiredSlice();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Premium::requestPremiumRequiredSlice() {
|
||||
_premiumRequiredRequestScheduled = false;
|
||||
if (!_resolvePremiumRequestedUsers.empty()
|
||||
|| _resolvePremiumRequiredUsers.empty()) {
|
||||
return;
|
||||
}
|
||||
constexpr auto kPerRequest = 100;
|
||||
auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers
|
||||
| ranges::views::transform(&UserData::inputUser));
|
||||
if (users.v.size() > kPerRequest) {
|
||||
auto shortened = users.v;
|
||||
shortened.resize(kPerRequest);
|
||||
users = MTP_vector<MTPInputUser>(std::move(shortened));
|
||||
const auto from = begin(_resolvePremiumRequiredUsers);
|
||||
_resolvePremiumRequestedUsers = { from, from + kPerRequest };
|
||||
_resolvePremiumRequiredUsers.erase(from, from + kPerRequest);
|
||||
} else {
|
||||
_resolvePremiumRequestedUsers
|
||||
= base::take(_resolvePremiumRequiredUsers);
|
||||
}
|
||||
const auto finish = [=](const QVector<MTPBool> &list) {
|
||||
constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite;
|
||||
constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown;
|
||||
constexpr auto mask = me | known;
|
||||
|
||||
auto index = 0;
|
||||
for (const auto &user : base::take(_resolvePremiumRequestedUsers)) {
|
||||
const auto require = (index < list.size())
|
||||
&& mtpIsTrue(list[index++]);
|
||||
user->setFlags((user->flags() & ~mask)
|
||||
| known
|
||||
| (require ? me : UserDataFlag()));
|
||||
}
|
||||
if (!_premiumRequiredRequestScheduled
|
||||
&& !_resolvePremiumRequiredUsers.empty()) {
|
||||
_premiumRequiredRequestScheduled = true;
|
||||
crl::on_main(_session, [=] {
|
||||
requestPremiumRequiredSlice();
|
||||
});
|
||||
}
|
||||
_somePremiumRequiredResolved.fire({});
|
||||
};
|
||||
_session->api().request(
|
||||
MTPusers_GetIsPremiumRequiredToContact(std::move(users))
|
||||
).done([=](const MTPVector<MTPBool> &result) {
|
||||
finish(result.v);
|
||||
}).fail([=] {
|
||||
finish({});
|
||||
}).send();
|
||||
}
|
||||
|
||||
PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null<PeerData*> peer)
|
||||
: _peer(peer)
|
||||
, _api(&peer->session().api().instance()) {
|
||||
@@ -494,4 +564,49 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const {
|
||||
false);
|
||||
}
|
||||
|
||||
RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||
not_null<PeerData*> peer,
|
||||
History *maybeHistory) {
|
||||
const auto user = peer->asUser();
|
||||
if (!user
|
||||
|| !user->someRequirePremiumToWrite()
|
||||
|| user->session().premium()) {
|
||||
return RequirePremiumState::No;
|
||||
} else if (user->requirePremiumToWriteKnown()) {
|
||||
return user->meRequiresPremiumToWrite()
|
||||
? RequirePremiumState::Yes
|
||||
: RequirePremiumState::No;
|
||||
} else if (user->flags() & UserDataFlag::MutualContact) {
|
||||
return RequirePremiumState::No;
|
||||
} else if (!maybeHistory) {
|
||||
return RequirePremiumState::Unknown;
|
||||
}
|
||||
|
||||
const auto update = [&](bool require) {
|
||||
using Flag = UserDataFlag;
|
||||
constexpr auto known = Flag::RequirePremiumToWriteKnown;
|
||||
constexpr auto me = Flag::MeRequiresPremiumToWrite;
|
||||
user->setFlags((user->flags() & ~me)
|
||||
| known
|
||||
| (require ? me : Flag()));
|
||||
};
|
||||
// We allow this potentially-heavy loop because in case we've opened
|
||||
// the chat and have a lot of messages `requires_premium` will be known.
|
||||
for (const auto &block : maybeHistory->blocks) {
|
||||
for (const auto &view : block->messages) {
|
||||
const auto item = view->data();
|
||||
if (!item->out() && !item->isService()) {
|
||||
update(false);
|
||||
return RequirePremiumState::No;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (user->isContact() // Here we know, that we're not in his contacts.
|
||||
&& maybeHistory->loadedAtTop() // And no incoming messages.
|
||||
&& maybeHistory->loadedAtBottom()) {
|
||||
update(true);
|
||||
}
|
||||
return RequirePremiumState::Unknown;
|
||||
}
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_subscription_option.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
class History;
|
||||
class ApiWrap;
|
||||
|
||||
namespace Main {
|
||||
@@ -103,10 +104,14 @@ public:
|
||||
[[nodiscard]] auto subscriptionOptions() const
|
||||
-> const Data::SubscriptionOptions &;
|
||||
|
||||
[[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const;
|
||||
void resolvePremiumRequired(not_null<UserData*> user);
|
||||
|
||||
private:
|
||||
void reloadPromo();
|
||||
void reloadStickers();
|
||||
void reloadCloudSet();
|
||||
void requestPremiumRequiredSlice();
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
@@ -143,6 +148,11 @@ private:
|
||||
|
||||
Data::SubscriptionOptions _subscriptionOptions;
|
||||
|
||||
rpl::event_stream<> _somePremiumRequiredResolved;
|
||||
base::flat_set<not_null<UserData*>> _resolvePremiumRequiredUsers;
|
||||
base::flat_set<not_null<UserData*>> _resolvePremiumRequestedUsers;
|
||||
bool _premiumRequiredRequestScheduled = false;
|
||||
|
||||
};
|
||||
|
||||
class PremiumGiftCodeOptions final {
|
||||
@@ -196,4 +206,13 @@ private:
|
||||
|
||||
};
|
||||
|
||||
enum class RequirePremiumState {
|
||||
Unknown,
|
||||
Yes,
|
||||
No,
|
||||
};
|
||||
[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite(
|
||||
not_null<PeerData*> peer,
|
||||
History *maybeHistory);
|
||||
|
||||
} // namespace Api
|
||||
|
||||
@@ -174,14 +174,13 @@ bool SendProgressManager::skipRequest(const Key &key) const {
|
||||
return true;
|
||||
}
|
||||
const auto recently = base::unixtime::now() - kSendTypingsToOfflineFor;
|
||||
const auto online = user->onlineTill;
|
||||
if (online == -2) { // last seen recently
|
||||
const auto lastseen = user->lastseen();
|
||||
if (lastseen.isRecently()) {
|
||||
return false;
|
||||
} else if (online < 0) {
|
||||
return (-online < recently);
|
||||
} else {
|
||||
return (online < recently);
|
||||
} else if (const auto value = lastseen.onlineTill()) {
|
||||
return (value < recently);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SendProgressManager::done(mtpRequestId requestId) {
|
||||
|
||||
@@ -402,7 +402,6 @@ void SendConfirmedFile(
|
||||
flags |= MessageFlag::HasReplyInfo;
|
||||
}
|
||||
const auto anonymousPost = peer->amAnonymous();
|
||||
const auto silentPost = ShouldSendSilent(peer, file->to.options);
|
||||
FillMessagePostFlags(action, peer, flags);
|
||||
if (file->to.options.scheduled) {
|
||||
flags |= MessageFlag::IsOrWasScheduled;
|
||||
|
||||
@@ -914,12 +914,6 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||
bool isOnlineOrig = Core::App().hasActiveWindow(&session());
|
||||
bool isOnline = settings->sendOnlinePackets && isOnlineOrig;
|
||||
|
||||
// AyuGram sendOfflinePacketAfterOnline
|
||||
if (settings->sendOfflinePacketAfterOnline && _lastWasOnline)
|
||||
{
|
||||
isOnline = false;
|
||||
}
|
||||
|
||||
int updateIn = config.onlineUpdatePeriod;
|
||||
Assert(updateIn >= 0);
|
||||
if (isOnline) {
|
||||
@@ -958,7 +952,9 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||
}
|
||||
|
||||
const auto self = session().user();
|
||||
self->onlineTill = base::unixtime::now() + (isOnline ? (config.onlineUpdatePeriod / 1000) : -1);
|
||||
const auto onlineFor = (config.onlineUpdatePeriod / 1000);
|
||||
self->updateLastseen(Data::LastseenStatus::OnlineTill(
|
||||
base::unixtime::now() + (isOnline ? onlineFor : -1)));
|
||||
session().changes().peerUpdated(
|
||||
self,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
@@ -972,24 +968,6 @@ void Updates::updateOnline(crl::time lastNonIdleTime, bool gotOtherOffline) {
|
||||
updateIn = qMin(updateIn, int(_lastSetOnline + config.onlineUpdatePeriod - ms));
|
||||
Assert(updateIn >= 0);
|
||||
}
|
||||
|
||||
// AyuGram sendOfflinePacketAfterOnline
|
||||
if (settings->sendOfflinePacketAfterOnline)
|
||||
{
|
||||
session().api().requestFullPeer(session().user());
|
||||
if (session().user()->onlineTill > base::unixtime::now())
|
||||
{
|
||||
DEBUG_LOG(("[AyuGram] User likely appeared online"));
|
||||
|
||||
_onlineRequest = api().request(MTPaccount_UpdateStatus(
|
||||
MTP_bool(true)
|
||||
)).send();
|
||||
}
|
||||
|
||||
DEBUG_LOG(("[AyuGram] Decreasing updateIn because of enabled features"));
|
||||
updateIn = 1250;
|
||||
}
|
||||
|
||||
_onlineTimer.callOnce(updateIn);
|
||||
}
|
||||
|
||||
@@ -1243,8 +1221,8 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
||||
if (user && !requestingDifference()) {
|
||||
user->madeAction(base::unixtime::now());
|
||||
}
|
||||
ClearMediaAsExpired(item);
|
||||
}
|
||||
ClearMediaAsExpired(item);
|
||||
}
|
||||
} else {
|
||||
// Perhaps it was an unread mention!
|
||||
@@ -1882,23 +1860,13 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
|
||||
case mtpc_updateUserStatus: {
|
||||
auto &d = update.c_updateUserStatus();
|
||||
if (auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
switch (d.vstatus().type()) {
|
||||
case mtpc_userStatusEmpty: user->onlineTill = 0; break;
|
||||
case mtpc_userStatusRecently:
|
||||
if (user->onlineTill > -10) { // don't modify pseudo-online
|
||||
user->onlineTill = -2;
|
||||
}
|
||||
break;
|
||||
case mtpc_userStatusLastWeek: user->onlineTill = -3; break;
|
||||
case mtpc_userStatusLastMonth: user->onlineTill = -4; break;
|
||||
case mtpc_userStatusOffline: user->onlineTill = d.vstatus().c_userStatusOffline().vwas_online().v; break;
|
||||
case mtpc_userStatusOnline: user->onlineTill = d.vstatus().c_userStatusOnline().vexpires().v; break;
|
||||
if (const auto user = session().data().userLoaded(d.vuser_id())) {
|
||||
const auto now = LastseenFromMTP(d.vstatus(), user->lastseen());
|
||||
if (user->updateLastseen(now)) {
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
}
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
session().data().maybeStopWatchForOffline(user);
|
||||
}
|
||||
if (UserId(d.vuser_id()) == session().userId()) {
|
||||
if (d.vstatus().type() == mtpc_userStatusOffline
|
||||
@@ -2540,6 +2508,10 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
||||
session().data().reactions().refreshRecentDelayed();
|
||||
} break;
|
||||
|
||||
case mtpc_updateSavedReactionTags: {
|
||||
session().data().reactions().refreshMyTagsDelayed();
|
||||
} break;
|
||||
|
||||
////// Cloud saved GIFs
|
||||
case mtpc_updateSavedGifs: {
|
||||
session().data().stickers().setLastSavedGifsUpdate(0);
|
||||
|
||||
@@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_who_reacted.h"
|
||||
|
||||
#include "api/api_global_privacy.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
@@ -19,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_message_reaction_id.h"
|
||||
#include "data/data_peer_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_app_config.h"
|
||||
#include "main/main_session.h"
|
||||
@@ -36,10 +38,11 @@ namespace {
|
||||
constexpr auto kContextReactionsLimit = 50;
|
||||
|
||||
using Data::ReactionId;
|
||||
using WhoReadState = Ui::WhoReadState;
|
||||
|
||||
struct Peers {
|
||||
std::vector<WhoReadPeer> list;
|
||||
bool unknown = false;
|
||||
WhoReadState state = WhoReadState::Empty;
|
||||
|
||||
friend inline bool operator==(
|
||||
const Peers &a,
|
||||
@@ -59,7 +62,7 @@ struct PeersWithReactions {
|
||||
std::vector<PeerWithReaction> list;
|
||||
std::vector<WhoReadPeer> read;
|
||||
int fullReactionsCount = 0;
|
||||
bool unknown = false;
|
||||
WhoReadState state = WhoReadState::Empty;
|
||||
|
||||
friend inline bool operator==(
|
||||
const PeersWithReactions &a,
|
||||
@@ -68,7 +71,7 @@ struct PeersWithReactions {
|
||||
|
||||
struct CachedRead {
|
||||
CachedRead()
|
||||
: data(Peers{ .unknown = true }) {
|
||||
: data(Peers{ .state = WhoReadState::Unknown }) {
|
||||
}
|
||||
rpl::variable<Peers> data;
|
||||
mtpRequestId requestId = 0;
|
||||
@@ -76,7 +79,7 @@ struct CachedRead {
|
||||
|
||||
struct CachedReacted {
|
||||
CachedReacted()
|
||||
: data(PeersWithReactions{ .unknown = true }) {
|
||||
: data(PeersWithReactions{ .state = WhoReadState::Unknown }) {
|
||||
}
|
||||
rpl::variable<PeersWithReactions> data;
|
||||
mtpRequestId requestId = 0;
|
||||
@@ -186,6 +189,27 @@ struct State {
|
||||
context->cachedReacted.erase(j);
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
Data::AmPremiumValue(
|
||||
session
|
||||
) | rpl::skip(1) | rpl::filter(
|
||||
rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
for (auto &[item, cache] : context->cachedRead) {
|
||||
if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
|
||||
cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
|
||||
}
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
session->api().globalPrivacy().hideReadTime(
|
||||
) | rpl::skip(1) | rpl::filter(
|
||||
!rpl::mappers::_1
|
||||
) | rpl::start_with_next([=] {
|
||||
for (auto &[item, cache] : context->cachedRead) {
|
||||
if (cache.data.current().state == Ui::WhoReadState::MyHidden) {
|
||||
cache.data = Peers{ .state = Ui::WhoReadState::Unknown };
|
||||
}
|
||||
}
|
||||
}, context->subscriptions[session]);
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -222,7 +246,38 @@ struct State {
|
||||
}
|
||||
const auto context = PreparedContextAt(weak.data(), session);
|
||||
auto &entry = context->cacheRead(item);
|
||||
if (!entry.requestId) {
|
||||
if (entry.requestId) {
|
||||
} else if (const auto user = item->history()->peer->asUser()) {
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetOutboxReadDate(
|
||||
user->input,
|
||||
MTP_int(item->id)
|
||||
)
|
||||
).done([=](const MTPOutboxReadDate &result) {
|
||||
const auto &data = result.data();
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
auto parsed = Peers();
|
||||
parsed.list.push_back({
|
||||
.peer = user->id,
|
||||
.date = data.vdate().v,
|
||||
});
|
||||
entry.data = std::move(parsed);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
const auto &text = error.type();
|
||||
entry.data = (text == u"YOUR_PRIVACY_RESTRICTED"_q)
|
||||
? Peers{ .state = WhoReadState::MyHidden }
|
||||
: (text == u"USER_PRIVACY_RESTRICTED"_q)
|
||||
? Peers{ .state = WhoReadState::HisHidden }
|
||||
: (text == u"MESSAGE_TOO_OLD"_q)
|
||||
? Peers{ .state = WhoReadState::TooOld }
|
||||
: Peers{ .state = WhoReadState::Empty };
|
||||
}
|
||||
}).send();
|
||||
} else {
|
||||
entry.requestId = session->api().request(
|
||||
MTPmessages_GetMessageReadParticipants(
|
||||
item->history()->peer->input,
|
||||
@@ -243,8 +298,8 @@ struct State {
|
||||
}).fail([=] {
|
||||
auto &entry = context->cacheRead(item);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = Peers();
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
entry.data = Peers{ .state = WhoReadState::Empty };
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
@@ -258,7 +313,7 @@ struct State {
|
||||
.list = peers.list | ranges::views::transform([](WhoReadPeer peer) {
|
||||
return PeerWithReaction{ .peerWithDate = peer };
|
||||
}) | ranges::to_vector,
|
||||
.unknown = peers.unknown,
|
||||
.state = peers.state,
|
||||
};
|
||||
result.read = std::move(peers.list);
|
||||
return result;
|
||||
@@ -319,8 +374,10 @@ struct State {
|
||||
}).fail([=] {
|
||||
auto &entry = context->cacheReacted(item, reaction);
|
||||
entry.requestId = 0;
|
||||
if (entry.data.current().unknown) {
|
||||
entry.data = PeersWithReactions();
|
||||
if (entry.data.current().state == WhoReadState::Unknown) {
|
||||
entry.data = PeersWithReactions{
|
||||
.state = WhoReadState::Empty,
|
||||
};
|
||||
}
|
||||
}).send();
|
||||
}
|
||||
@@ -336,8 +393,9 @@ struct State {
|
||||
WhoReactedIds(item, {}, context),
|
||||
WhoReadIds(item, context)
|
||||
) | rpl::map([=](PeersWithReactions &&reacted, Peers &&read) {
|
||||
if (reacted.unknown || read.unknown) {
|
||||
return PeersWithReactions{ .unknown = true };
|
||||
if (reacted.state == WhoReadState::Unknown
|
||||
|| read.state == WhoReadState::Unknown) {
|
||||
return PeersWithReactions{ .state = WhoReadState::Unknown};
|
||||
}
|
||||
auto &list = reacted.list;
|
||||
for (const auto &peerWithDate : read.list) {
|
||||
@@ -531,16 +589,17 @@ rpl::producer<Ui::WhoReadContent> WhoReacted(
|
||||
std::move(
|
||||
idsWithReactions
|
||||
) | rpl::start_with_next([=](PeersWithReactions &&peers) {
|
||||
if (peers.unknown) {
|
||||
if (peers.state == WhoReadState::Unknown) {
|
||||
state->userpics.clear();
|
||||
consumer.put_next(Ui::WhoReadContent{
|
||||
.type = state->current.type,
|
||||
.fullReactionsCount = state->current.fullReactionsCount,
|
||||
.fullReadCount = state->current.fullReadCount,
|
||||
.unknown = true,
|
||||
.state = WhoReadState::Unknown,
|
||||
});
|
||||
return;
|
||||
}
|
||||
state->current.state = peers.state;
|
||||
state->current.fullReadCount = int(peers.read.size());
|
||||
state->current.fullReactionsCount = peers.fullReactionsCount;
|
||||
if (whoReadIds) {
|
||||
@@ -631,6 +690,22 @@ bool WhoReadExists(not_null<HistoryItem*> item) {
|
||||
}
|
||||
const auto history = item->history();
|
||||
const auto peer = history->peer;
|
||||
if (const auto user = peer->asUser()) {
|
||||
if (user->isSelf()
|
||||
|| user->isBot()
|
||||
|| user->isServiceUser()
|
||||
|| user->readDatesPrivate()) {
|
||||
return false;
|
||||
}
|
||||
const auto &appConfig = peer->session().account().appConfig();
|
||||
const auto expirePeriod = appConfig.get<int>(
|
||||
"pm_read_date_expire_period",
|
||||
7 * 86400);
|
||||
if (item->date() + int64(expirePeriod) <= int64(base::unixtime::now())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
const auto chat = peer->asChat();
|
||||
const auto megagroup = peer->asMegagroup();
|
||||
if ((!chat && !megagroup)
|
||||
|
||||
@@ -85,6 +85,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
|
||||
// AyuGram includes
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/ayu_worker.h"
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -1953,28 +1954,28 @@ void ApiWrap::saveDraftToCloudDelayed(not_null<Data::Thread*> thread) {
|
||||
|
||||
void ApiWrap::updatePrivacyLastSeens() {
|
||||
const auto now = base::unixtime::now();
|
||||
_session->data().enumerateUsers([&](UserData *user) {
|
||||
if (user->isSelf() || !user->isLoaded()) {
|
||||
return;
|
||||
}
|
||||
if (user->onlineTill <= 0) {
|
||||
return;
|
||||
}
|
||||
if (!_session->premium()) {
|
||||
_session->data().enumerateUsers([&](not_null<UserData*> user) {
|
||||
if (user->isSelf()
|
||||
|| !user->isLoaded()
|
||||
|| user->lastseen().isHidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (user->onlineTill + 3 * 86400 >= now) {
|
||||
user->onlineTill = -2; // recently
|
||||
} else if (user->onlineTill + 7 * 86400 >= now) {
|
||||
user->onlineTill = -3; // last week
|
||||
} else if (user->onlineTill + 30 * 86400 >= now) {
|
||||
user->onlineTill = -4; // last month
|
||||
} else {
|
||||
user->onlineTill = 0;
|
||||
}
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
session().data().maybeStopWatchForOffline(user);
|
||||
});
|
||||
const auto till = user->lastseen().onlineTill();
|
||||
user->updateLastseen((till + 3 * 86400 >= now)
|
||||
? Data::LastseenStatus::Recently(true)
|
||||
: (till + 7 * 86400 >= now)
|
||||
? Data::LastseenStatus::WithinWeek(true)
|
||||
: (till + 30 * 86400 >= now)
|
||||
? Data::LastseenStatus::WithinMonth(true)
|
||||
: Data::LastseenStatus::LongAgo(true));
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
session().data().maybeStopWatchForOffline(user);
|
||||
});
|
||||
}
|
||||
|
||||
if (_contactsStatusesRequestId) {
|
||||
request(_contactsStatusesRequestId).cancel();
|
||||
@@ -1982,20 +1983,17 @@ void ApiWrap::updatePrivacyLastSeens() {
|
||||
_contactsStatusesRequestId = request(MTPcontacts_GetStatuses(
|
||||
)).done([=](const MTPVector<MTPContactStatus> &result) {
|
||||
_contactsStatusesRequestId = 0;
|
||||
for (const auto &item : result.v) {
|
||||
Assert(item.type() == mtpc_contactStatus);
|
||||
auto &data = item.c_contactStatus();
|
||||
if (auto user = _session->data().userLoaded(data.vuser_id())) {
|
||||
auto oldOnlineTill = user->onlineTill;
|
||||
auto newOnlineTill = OnlineTillFromStatus(
|
||||
for (const auto &status : result.v) {
|
||||
const auto &data = status.data();
|
||||
const auto userId = UserId(data.vuser_id());
|
||||
if (const auto user = _session->data().userLoaded(userId)) {
|
||||
const auto status = LastseenFromMTP(
|
||||
data.vstatus(),
|
||||
oldOnlineTill);
|
||||
if (oldOnlineTill != newOnlineTill) {
|
||||
user->onlineTill = newOnlineTill;
|
||||
user->lastseen());
|
||||
if (user->updateLastseen(status)) {
|
||||
session().changes().peerUpdated(
|
||||
user,
|
||||
Data::PeerUpdate::Flag::OnlineStatus);
|
||||
session().data().maybeStopWatchForOffline(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2004,22 +2002,6 @@ void ApiWrap::updatePrivacyLastSeens() {
|
||||
}).send();
|
||||
}
|
||||
|
||||
int ApiWrap::OnlineTillFromStatus(
|
||||
const MTPUserStatus &status,
|
||||
int currentOnlineTill) {
|
||||
switch (status.type()) {
|
||||
case mtpc_userStatusEmpty: return 0;
|
||||
case mtpc_userStatusRecently:
|
||||
// Don't modify pseudo-online.
|
||||
return (currentOnlineTill > -10) ? -2 : currentOnlineTill;
|
||||
case mtpc_userStatusLastWeek: return -3;
|
||||
case mtpc_userStatusLastMonth: return -4;
|
||||
case mtpc_userStatusOffline: return status.c_userStatusOffline().vwas_online().v;
|
||||
case mtpc_userStatusOnline: return status.c_userStatusOnline().vexpires().v;
|
||||
}
|
||||
Unexpected("Bad UserStatus type.");
|
||||
}
|
||||
|
||||
void ApiWrap::clearHistory(not_null<PeerData*> peer, bool revoke) {
|
||||
deleteHistory(peer, true, revoke);
|
||||
}
|
||||
@@ -3585,8 +3567,7 @@ void ApiWrap::sendUploadedPhoto(
|
||||
if (const auto item = _session->data().message(localId)) {
|
||||
// AyuGram useScheduledMessages
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (settings->useScheduledMessages && !options.scheduled)
|
||||
{
|
||||
if (settings->useScheduledMessages && !options.scheduled) {
|
||||
DEBUG_LOG(("[AyuGram] Scheduling message"));
|
||||
auto current = base::unixtime::now();
|
||||
options.scheduled = current + 18; // using 18 seconds because photo can be big
|
||||
@@ -3612,8 +3593,7 @@ void ApiWrap::sendUploadedDocument(
|
||||
|
||||
// AyuGram useScheduledMessages
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (settings->useScheduledMessages && !options.scheduled)
|
||||
{
|
||||
if (settings->useScheduledMessages && !options.scheduled) {
|
||||
DEBUG_LOG(("[AyuGram] Scheduling message"));
|
||||
auto current = base::unixtime::now();
|
||||
options.scheduled = current + 60; // well, a document can be really big...
|
||||
@@ -3800,6 +3780,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
|
||||
draftTopicRootId,
|
||||
UnixtimeFromMsgId(response.outerMsgId));
|
||||
}
|
||||
|
||||
AyuWorker::markAsOnline(_session);
|
||||
};
|
||||
const auto fail = [=](
|
||||
const MTP::Error &error,
|
||||
@@ -3893,6 +3875,8 @@ void ApiWrap::sendBotStart(
|
||||
MTP_string(token)
|
||||
)).done([=](const MTPUpdates &result) {
|
||||
applyUpdates(result);
|
||||
|
||||
AyuWorker::markAsOnline(_session);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (chat) {
|
||||
const auto type = error.type();
|
||||
@@ -4092,8 +4076,7 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
Fn<void(bool)> done) {
|
||||
// AyuGram useScheduledMessages
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (settings->useScheduledMessages && !options.scheduled)
|
||||
{
|
||||
if (settings->useScheduledMessages && !options.scheduled) {
|
||||
DEBUG_LOG(("[AyuGram] Scheduling message"));
|
||||
auto current = base::unixtime::now();
|
||||
options.scheduled = current + 12;
|
||||
@@ -4144,6 +4127,8 @@ void ApiWrap::sendMediaWithRandomId(
|
||||
if (updateRecentStickers) {
|
||||
requestRecentStickersForce(true);
|
||||
}
|
||||
|
||||
AyuWorker::markAsOnline(_session);
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (done) done(false);
|
||||
sendMessageFail(error, peer, randomId, itemId);
|
||||
@@ -4192,8 +4177,7 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
|
||||
// AyuGram useScheduledMessages
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (settings->useScheduledMessages && !album->options.scheduled)
|
||||
{
|
||||
if (settings->useScheduledMessages && !album->options.scheduled) {
|
||||
DEBUG_LOG(("[AyuGram] Scheduling message"));
|
||||
auto current = base::unixtime::now();
|
||||
album->options.scheduled = current + 12;
|
||||
@@ -4249,6 +4233,8 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
|
||||
(sendAs ? sendAs->input : MTP_inputPeerEmpty())
|
||||
), [=](const MTPUpdates &result, const MTP::Response &response) {
|
||||
_sendingAlbums.remove(groupId);
|
||||
|
||||
AyuWorker::markAsOnline(_session);
|
||||
}, [=](const MTP::Error &error, const MTP::Response &response) {
|
||||
if (const auto album = _sendingAlbums.take(groupId)) {
|
||||
for (const auto &item : (*album)->items) {
|
||||
|
||||
@@ -258,10 +258,6 @@ public:
|
||||
void updateNotifySettingsDelayed(Data::DefaultNotify type);
|
||||
void saveDraftToCloudDelayed(not_null<Data::Thread*> thread);
|
||||
|
||||
static int OnlineTillFromStatus(
|
||||
const MTPUserStatus &status,
|
||||
int currentOnlineTill);
|
||||
|
||||
void clearHistory(not_null<PeerData*> peer, bool revoke);
|
||||
void deleteConversation(not_null<PeerData*> peer, bool revoke);
|
||||
|
||||
|
||||
@@ -3,14 +3,11 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
// https://github.com/AyuGram/AyuGram4AX/blob/rewrite/TMessagesProj/src/main/java/com/radolyn/ayugram/AyuConstants.java
|
||||
constexpr int DOCUMENT_TYPE_NONE = 0;
|
||||
|
||||
constexpr int DOCUMENT_TYPE_PHOTO = 1;
|
||||
|
||||
constexpr int DOCUMENT_TYPE_STICKER = 2;
|
||||
|
||||
constexpr int DOCUMENT_TYPE_FILE = 3;
|
||||
|
||||
@@ -3,21 +3,19 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_infra.h"
|
||||
|
||||
#include "ayu_lang.h"
|
||||
#include "ayu/ayu_lottie.h"
|
||||
#include "ayu/ui/ayu_lottie.h"
|
||||
#include "ayu/database/ayu_database.h"
|
||||
#include "lang/lang_instance.h"
|
||||
#include "ayu_worker.h"
|
||||
#include "ayu/ayu_fonts.h"
|
||||
#include "ayu/ayu_settings.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "lang/lang_instance.h"
|
||||
|
||||
namespace AyuInfra
|
||||
{
|
||||
namespace AyuInfra {
|
||||
|
||||
void initLang()
|
||||
{
|
||||
void initLang() {
|
||||
QString langPackBaseId = Lang::GetInstance().baseId();
|
||||
QString langPackId = Lang::GetInstance().id();
|
||||
if (langPackId.isEmpty()) {
|
||||
@@ -28,30 +26,26 @@ void initLang()
|
||||
CustomLangPack::currentInstance()->fetchCustomLangPack(langPackId, langPackBaseId);
|
||||
}
|
||||
|
||||
void initLottie()
|
||||
{
|
||||
AyuUi::setLottieImpl(std::make_shared<AyuUi::AyuLottieImpl>());
|
||||
}
|
||||
|
||||
void initDatabase()
|
||||
{
|
||||
AyuDatabase::initialize();
|
||||
}
|
||||
|
||||
void initFonts()
|
||||
{
|
||||
void initFonts() {
|
||||
auto settings = &AyuSettings::getInstance();
|
||||
|
||||
AyuFonts::setMainFont(settings->mainFont);
|
||||
AyuFonts::setMonoFont(settings->monoFont);
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
initLang();
|
||||
initLottie();
|
||||
initFonts();
|
||||
initDatabase();
|
||||
void initDatabase() {
|
||||
AyuDatabase::initialize();
|
||||
}
|
||||
|
||||
}
|
||||
void initWorker() {
|
||||
AyuWorker::initialize();
|
||||
}
|
||||
|
||||
void init() {
|
||||
initLang();
|
||||
initDatabase();
|
||||
initFonts();
|
||||
initWorker();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
namespace AyuInfra
|
||||
{
|
||||
namespace AyuInfra {
|
||||
|
||||
void init();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_lang.h"
|
||||
|
||||
#include "qjsondocument.h"
|
||||
@@ -24,26 +24,22 @@ CustomLangPack *CustomLangPack::instance = nullptr;
|
||||
|
||||
CustomLangPack::CustomLangPack() = default;
|
||||
|
||||
void CustomLangPack::initInstance()
|
||||
{
|
||||
if (!instance)
|
||||
instance = new CustomLangPack;
|
||||
void CustomLangPack::initInstance() {
|
||||
if (!instance) instance = new CustomLangPack;
|
||||
}
|
||||
|
||||
CustomLangPack *CustomLangPack::currentInstance()
|
||||
{
|
||||
CustomLangPack *CustomLangPack::currentInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
void CustomLangPack::fetchCustomLangPack(const QString &langPackId, const QString &langPackBaseId)
|
||||
{
|
||||
void CustomLangPack::fetchCustomLangPack(const QString &langPackId, const QString &langPackBaseId) {
|
||||
LOG(("Current Language pack ID: %1, Base ID: %2").arg(langPackId, langPackBaseId));
|
||||
|
||||
auto finalLangPackId = langMapping.contains(langPackId) ? langMapping[langPackId] : langPackId;
|
||||
|
||||
const auto proxy = Core::App().settings().proxy().isEnabled()
|
||||
? Core::App().settings().proxy().selected()
|
||||
: MTP::ProxyData();
|
||||
? Core::App().settings().proxy().selected()
|
||||
: MTP::ProxyData();
|
||||
if (proxy.type == MTP::ProxyData::Type::Socks5 || proxy.type == MTP::ProxyData::Type::Http) {
|
||||
QNetworkProxy LocaleProxy = ToNetworkProxy(ToDirectIpProxy(proxy));
|
||||
networkManager.setProxy(LocaleProxy);
|
||||
@@ -55,8 +51,7 @@ void CustomLangPack::fetchCustomLangPack(const QString &langPackId, const QStrin
|
||||
if (!finalLangPackId.isEmpty() && !langPackBaseId.isEmpty() && !needFallback) {
|
||||
url.setUrl(qsl("https://cdn.jsdelivr.net/gh/AyuGram/Languages@l10n_main/values/langs/%1/Shared.json").arg(
|
||||
finalLangPackId));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
url.setUrl(qsl("https://cdn.jsdelivr.net/gh/AyuGram/Languages@l10n_main/values/langs/%1/Shared.json").arg(
|
||||
needFallback ? langPackBaseId : finalLangPackId));
|
||||
}
|
||||
@@ -67,8 +62,7 @@ void CustomLangPack::fetchCustomLangPack(const QString &langPackId, const QStrin
|
||||
needFallback ? (langPackBaseId.isEmpty() ? finalLangPackId : langPackBaseId) : finalLangPackId));
|
||||
}
|
||||
|
||||
void CustomLangPack::fetchFinished()
|
||||
{
|
||||
void CustomLangPack::fetchFinished() {
|
||||
if (!_chkReply) return;
|
||||
|
||||
QString langPackBaseId = Lang::GetInstance().baseId();
|
||||
@@ -80,15 +74,13 @@ void CustomLangPack::fetchFinished()
|
||||
needFallback = true;
|
||||
_chkReply->disconnect();
|
||||
fetchCustomLangPack("", langPackBaseId);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
QByteArray result = _chkReply->readAll().trimmed();
|
||||
QJsonParseError error{};
|
||||
QJsonDocument str = QJsonDocument::fromJson(result, &error);
|
||||
if (error.error == QJsonParseError::NoError) {
|
||||
parseLangFile(str);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
LOG(("Incorrect JSON File. Fallback to default language: English..."));
|
||||
loadDefaultLangFile();
|
||||
}
|
||||
@@ -97,8 +89,7 @@ void CustomLangPack::fetchFinished()
|
||||
}
|
||||
}
|
||||
|
||||
void CustomLangPack::fetchError(QNetworkReply::NetworkError e)
|
||||
{
|
||||
void CustomLangPack::fetchError(QNetworkReply::NetworkError e) {
|
||||
LOG(("Network error: %1").arg(e));
|
||||
|
||||
if (e == QNetworkReply::NetworkError::ContentNotFoundError) {
|
||||
@@ -110,8 +101,7 @@ void CustomLangPack::fetchError(QNetworkReply::NetworkError e)
|
||||
needFallback = true;
|
||||
_chkReply->disconnect();
|
||||
fetchCustomLangPack("", langPackBaseId);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
LOG(("AyuGram Language pack not found! Fallback to default language: English..."));
|
||||
loadDefaultLangFile();
|
||||
_chkReply = nullptr;
|
||||
@@ -119,8 +109,7 @@ void CustomLangPack::fetchError(QNetworkReply::NetworkError e)
|
||||
}
|
||||
}
|
||||
|
||||
void CustomLangPack::loadDefaultLangFile()
|
||||
{
|
||||
void CustomLangPack::loadDefaultLangFile() {
|
||||
QFile file(":/localization/en.json");
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
QJsonDocument str = QJsonDocument::fromJson(file.readAll());
|
||||
@@ -133,8 +122,7 @@ void CustomLangPack::loadDefaultLangFile()
|
||||
}
|
||||
}
|
||||
|
||||
void CustomLangPack::parseLangFile(QJsonDocument str)
|
||||
{
|
||||
void CustomLangPack::parseLangFile(QJsonDocument str) {
|
||||
QJsonObject json = str.object();
|
||||
for (const QString &brokenKey : json.keys()) {
|
||||
auto key = qsl("ayu_") + brokenKey;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
@@ -11,31 +11,24 @@
|
||||
|
||||
class CustomLangPack : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(CustomLangPack)
|
||||
|
||||
public:
|
||||
static CustomLangPack *currentInstance();
|
||||
|
||||
static void initInstance();
|
||||
|
||||
static CustomLangPack *instance;
|
||||
|
||||
void fetchCustomLangPack(const QString &langPackId, const QString &langPackBaseId);
|
||||
|
||||
void loadDefaultLangFile();
|
||||
|
||||
void parseLangFile(QJsonDocument str);
|
||||
|
||||
public Q_SLOTS:
|
||||
void fetchFinished();
|
||||
|
||||
void fetchError(QNetworkReply::NetworkError e);
|
||||
|
||||
private:
|
||||
CustomLangPack();
|
||||
|
||||
~CustomLangPack() override = default;
|
||||
|
||||
QNetworkAccessManager networkManager;
|
||||
|
||||
@@ -3,56 +3,56 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_settings.h"
|
||||
|
||||
#include "ayu/ui/ayu_logo.h"
|
||||
|
||||
#include "lang_auto.h"
|
||||
#include "core/application.h"
|
||||
|
||||
#include "rpl/lifetime.h"
|
||||
#include "rpl/producer.h"
|
||||
#include "rpl/variable.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "ayu_worker.h"
|
||||
#include "window/window_controller.h"
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
namespace AyuSettings
|
||||
{
|
||||
namespace AyuSettings {
|
||||
|
||||
const std::string filename = "tdata/ayu_settings.json";
|
||||
|
||||
std::optional<AyuGramSettings> settings = std::nullopt;
|
||||
|
||||
rpl::variable<bool> sendReadMessagesReactive;
|
||||
|
||||
rpl::variable<bool> sendReadStoriesReactive;
|
||||
|
||||
rpl::variable<bool> sendOnlinePacketsReactive;
|
||||
|
||||
rpl::variable<bool> sendUploadProgressReactive;
|
||||
|
||||
rpl::variable<bool> sendOfflinePacketAfterOnlineReactive;
|
||||
|
||||
rpl::variable<QString> deletedMarkReactive;
|
||||
rpl::variable<bool> ghostModeEnabled;
|
||||
|
||||
rpl::variable<QString> deletedMarkReactive;
|
||||
rpl::variable<QString> editedMarkReactive;
|
||||
|
||||
rpl::variable<int> showPeerIdReactive;
|
||||
|
||||
rpl::variable<bool> ghostModeEnabled;
|
||||
|
||||
rpl::lifetime lifetime = rpl::lifetime();
|
||||
|
||||
bool ghostModeEnabled_util(AyuGramSettings &settingsUtil)
|
||||
{
|
||||
bool ghostModeEnabled_util(const AyuGramSettings &settingsUtil) {
|
||||
return
|
||||
!settingsUtil.sendReadMessages
|
||||
&& !settingsUtil.sendReadStories
|
||||
&& !settingsUtil.sendOnlinePackets
|
||||
&& !settingsUtil.sendUploadProgress
|
||||
&& settingsUtil.sendOfflinePacketAfterOnline;
|
||||
&& !settingsUtil.sendReadStories
|
||||
&& !settingsUtil.sendOnlinePackets
|
||||
&& !settingsUtil.sendUploadProgress
|
||||
&& settingsUtil.sendOfflinePacketAfterOnline;
|
||||
}
|
||||
|
||||
void initialize()
|
||||
{
|
||||
void initialize() {
|
||||
if (settings.has_value()) {
|
||||
return;
|
||||
}
|
||||
@@ -68,7 +68,8 @@ void initialize()
|
||||
{
|
||||
ghostModeEnabled =
|
||||
ghostModeEnabled_util(settings.value());
|
||||
}, lifetime);
|
||||
},
|
||||
lifetime);
|
||||
// ..
|
||||
sendReadStoriesReactive.value() | rpl::filter(
|
||||
[=](bool val)
|
||||
@@ -79,7 +80,8 @@ void initialize()
|
||||
{
|
||||
ghostModeEnabled =
|
||||
ghostModeEnabled_util(settings.value());
|
||||
}, lifetime);
|
||||
},
|
||||
lifetime);
|
||||
// ..
|
||||
sendOnlinePacketsReactive.value() | rpl::filter(
|
||||
[=](bool val)
|
||||
@@ -90,8 +92,9 @@ void initialize()
|
||||
{
|
||||
ghostModeEnabled =
|
||||
ghostModeEnabled_util(settings
|
||||
.value());
|
||||
}, lifetime);
|
||||
.value());
|
||||
},
|
||||
lifetime);
|
||||
// ..
|
||||
sendUploadProgressReactive.value() | rpl::filter(
|
||||
[=](bool val)
|
||||
@@ -102,8 +105,9 @@ void initialize()
|
||||
{
|
||||
ghostModeEnabled =
|
||||
ghostModeEnabled_util(settings
|
||||
.value());
|
||||
}, lifetime);
|
||||
.value());
|
||||
},
|
||||
lifetime);
|
||||
// ..
|
||||
sendOfflinePacketAfterOnlineReactive.value() | rpl::filter(
|
||||
[=](bool val)
|
||||
@@ -116,11 +120,11 @@ void initialize()
|
||||
ghostModeEnabled =
|
||||
ghostModeEnabled_util(
|
||||
settings.value());
|
||||
}, lifetime);
|
||||
},
|
||||
lifetime);
|
||||
}
|
||||
|
||||
void postinitialize()
|
||||
{
|
||||
void postinitialize() {
|
||||
sendReadMessagesReactive = settings->sendReadMessages;
|
||||
sendReadStoriesReactive = settings->sendReadStories;
|
||||
sendUploadProgressReactive = settings->sendUploadProgress;
|
||||
@@ -134,14 +138,12 @@ void postinitialize()
|
||||
ghostModeEnabled = ghostModeEnabled_util(settings.value());
|
||||
}
|
||||
|
||||
AyuGramSettings &getInstance()
|
||||
{
|
||||
AyuGramSettings &getInstance() {
|
||||
initialize();
|
||||
return settings.value();
|
||||
}
|
||||
|
||||
void load()
|
||||
{
|
||||
void load() {
|
||||
std::ifstream file(filename);
|
||||
if (!file.good()) {
|
||||
return;
|
||||
@@ -156,19 +158,16 @@ void load()
|
||||
|
||||
try {
|
||||
settings = p.get<AyuGramSettings>();
|
||||
}
|
||||
catch (...) {
|
||||
} catch (...) {
|
||||
LOG(("AyuGramSettings: failed to parse settings file"));
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
} catch (...) {
|
||||
LOG(("AyuGramSettings: failed to read settings file (not json-like)"));
|
||||
}
|
||||
postinitialize();
|
||||
}
|
||||
|
||||
void save()
|
||||
{
|
||||
void save() {
|
||||
initialize();
|
||||
|
||||
json p = settings.value();
|
||||
@@ -181,265 +180,300 @@ void save()
|
||||
postinitialize();
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendReadMessages(bool val)
|
||||
{
|
||||
AyuGramSettings::AyuGramSettings() {
|
||||
// ~ Ghost essentials
|
||||
sendReadMessages = true;
|
||||
sendReadStories = true;
|
||||
sendOnlinePackets = true;
|
||||
sendUploadProgress = true;
|
||||
sendOfflinePacketAfterOnline = false;
|
||||
|
||||
markReadAfterSend = true;
|
||||
markReadAfterReaction = true;
|
||||
markReadAfterPoll = true;
|
||||
useScheduledMessages = false;
|
||||
|
||||
// ~ Message edits & deletion history
|
||||
saveDeletedMessages = false;
|
||||
saveMessagesHistory = true;
|
||||
|
||||
// ~ QoL toggles
|
||||
disableAds = true;
|
||||
disableStories = false;
|
||||
collapseSimilarChannels = true;
|
||||
hideSimilarChannels = false;
|
||||
|
||||
uploadSpeedBoost = false;
|
||||
|
||||
disableNotificationsDelay = false;
|
||||
localPremium = false;
|
||||
copyUsernameAsLink = true;
|
||||
|
||||
// ~ Customization
|
||||
appIcon = AyuAssets::DEFAULT_ICON;
|
||||
simpleQuotesAndReplies = true;
|
||||
deletedMark = "🧹";
|
||||
editedMark = Core::IsAppLaunched() ? tr::lng_edited(tr::now) : QString("edited");
|
||||
recentStickersCount = 50;
|
||||
|
||||
// context menu items
|
||||
// 0 - hide
|
||||
// 1 - show normally
|
||||
// 2 - show with SHIFT or CTRL pressed
|
||||
showReactionsPanelInContextMenu = 1;
|
||||
showViewsPanelInContextMenu = 1;
|
||||
showHideMessageInContextMenu = 0;
|
||||
showUserMessagesInContextMenu = 2;
|
||||
showMessageDetailsInContextMenu = 2;
|
||||
|
||||
showLReadToggleInDrawer = true;
|
||||
showSReadToggleInDrawer = true;
|
||||
showGhostToggleInDrawer = true;
|
||||
showStreamerToggleInDrawer = false;
|
||||
|
||||
showGhostToggleInTray = true;
|
||||
showStreamerToggleInTray = false;
|
||||
|
||||
mainFont = "";
|
||||
monoFont = "";
|
||||
|
||||
hideNotificationCounters = false;
|
||||
hideAllChatsFolder = false;
|
||||
|
||||
/*
|
||||
* showPeerId = 0 means no ID shown
|
||||
* showPeerId = 1 means ID shown as for Telegram API devs
|
||||
* showPeerId = 2 means ID shown as for Bot API devs (-100)
|
||||
*/
|
||||
showPeerId = 2;
|
||||
showMessageSeconds = false;
|
||||
showMessageShot = true;
|
||||
|
||||
// ~ Confirmations
|
||||
stickerConfirmation = false;
|
||||
gifConfirmation = false;
|
||||
voiceConfirmation = false;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendReadMessages(bool val) {
|
||||
sendReadMessages = val;
|
||||
sendReadMessagesReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendReadStories(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_sendReadStories(bool val) {
|
||||
sendReadStories = val;
|
||||
sendReadStoriesReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendOnlinePackets(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_sendOnlinePackets(bool val) {
|
||||
sendOnlinePackets = val;
|
||||
sendOnlinePacketsReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendUploadProgress(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_sendUploadProgress(bool val) {
|
||||
sendUploadProgress = val;
|
||||
sendUploadProgressReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_sendOfflinePacketAfterOnline(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_sendOfflinePacketAfterOnline(bool val) {
|
||||
sendOfflinePacketAfterOnline = val;
|
||||
sendOfflinePacketAfterOnlineReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_ghostModeEnabled(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_ghostModeEnabled(bool val) {
|
||||
set_sendReadMessages(!val);
|
||||
set_sendReadStories(!val);
|
||||
set_sendOnlinePackets(!val);
|
||||
set_sendUploadProgress(!val);
|
||||
set_sendOfflinePacketAfterOnline(val);
|
||||
|
||||
if (const auto window = Core::App().activeWindow()) {
|
||||
if (const auto session = window->maybeSession()) {
|
||||
AyuWorker::markAsOnline(session); // mark as online to get offline instantly
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_markReadAfterSend(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_markReadAfterSend(bool val) {
|
||||
markReadAfterSend = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_markReadAfterReaction(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_markReadAfterReaction(bool val) {
|
||||
markReadAfterReaction = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_markReadAfterPoll(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_markReadAfterPoll(bool val) {
|
||||
markReadAfterPoll = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_useScheduledMessages(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_useScheduledMessages(bool val) {
|
||||
useScheduledMessages = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_saveDeletedMessages(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_saveDeletedMessages(bool val) {
|
||||
saveDeletedMessages = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_saveMessagesHistory(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_saveMessagesHistory(bool val) {
|
||||
saveMessagesHistory = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_disableAds(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_disableAds(bool val) {
|
||||
disableAds = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_disableStories(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_disableStories(bool val) {
|
||||
disableStories = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_collapseSimilarChannels(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_collapseSimilarChannels(bool val) {
|
||||
collapseSimilarChannels = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_hideSimilarChannels(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_hideSimilarChannels(bool val) {
|
||||
hideSimilarChannels = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_uploadSpeedBoost(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_uploadSpeedBoost(bool val) {
|
||||
uploadSpeedBoost = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_disableNotificationsDelay(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_disableNotificationsDelay(bool val) {
|
||||
disableNotificationsDelay = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_localPremium(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_localPremium(bool val) {
|
||||
localPremium = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_copyUsernameAsLink(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_copyUsernameAsLink(bool val) {
|
||||
copyUsernameAsLink = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_appIcon(QString val)
|
||||
{
|
||||
void AyuGramSettings::set_appIcon(QString val) {
|
||||
appIcon = std::move(val);
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_simpleQuotesAndReplies(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_simpleQuotesAndReplies(bool val) {
|
||||
simpleQuotesAndReplies = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_deletedMark(QString val)
|
||||
{
|
||||
void AyuGramSettings::set_deletedMark(QString val) {
|
||||
deletedMark = std::move(val);
|
||||
deletedMarkReactive = deletedMark;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_editedMark(QString val)
|
||||
{
|
||||
void AyuGramSettings::set_editedMark(QString val) {
|
||||
editedMark = std::move(val);
|
||||
editedMarkReactive = editedMark;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_recentStickersCount(int val)
|
||||
{
|
||||
void AyuGramSettings::set_recentStickersCount(int val) {
|
||||
recentStickersCount = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showReactionsPanelInContextMenu(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showReactionsPanelInContextMenu(int val) {
|
||||
showReactionsPanelInContextMenu = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showViewsPanelInContextMenu(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showViewsPanelInContextMenu(int val) {
|
||||
showViewsPanelInContextMenu = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showHideMessageInContextMenu(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showHideMessageInContextMenu(int val) {
|
||||
showHideMessageInContextMenu = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showUserMessagesInContextMenu(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showUserMessagesInContextMenu(int val) {
|
||||
showUserMessagesInContextMenu = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showMessageDetailsInContextMenu(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showMessageDetailsInContextMenu(int val) {
|
||||
showMessageDetailsInContextMenu = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showLReadToggleInDrawer(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showLReadToggleInDrawer(bool val) {
|
||||
showLReadToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showSReadToggleInDrawer(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showSReadToggleInDrawer(bool val) {
|
||||
showSReadToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showGhostToggleInDrawer(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showGhostToggleInDrawer(bool val) {
|
||||
showGhostToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showStreamerToggleInDrawer(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showStreamerToggleInDrawer(bool val) {
|
||||
showStreamerToggleInDrawer = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showGhostToggleInTray(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showGhostToggleInTray(bool val) {
|
||||
showGhostToggleInTray = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showStreamerToggleInTray(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showStreamerToggleInTray(bool val) {
|
||||
showStreamerToggleInTray = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_mainFont(QString val)
|
||||
{
|
||||
void AyuGramSettings::set_mainFont(QString val) {
|
||||
mainFont = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_monoFont(QString val)
|
||||
{
|
||||
void AyuGramSettings::set_monoFont(QString val) {
|
||||
monoFont = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showPeerId(int val)
|
||||
{
|
||||
void AyuGramSettings::set_showPeerId(int val) {
|
||||
showPeerId = val;
|
||||
showPeerIdReactive = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_hideNotificationCounters(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_hideNotificationCounters(bool val) {
|
||||
hideNotificationCounters = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_hideAllChatsFolder(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_hideAllChatsFolder(bool val) {
|
||||
hideAllChatsFolder = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_showMessageSeconds(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showMessageSeconds(bool val) {
|
||||
showMessageSeconds = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_stickerConfirmation(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_showMessageShot(bool val) {
|
||||
showMessageShot = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_stickerConfirmation(bool val) {
|
||||
stickerConfirmation = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_gifConfirmation(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_gifConfirmation(bool val) {
|
||||
gifConfirmation = val;
|
||||
}
|
||||
|
||||
void AyuGramSettings::set_voiceConfirmation(bool val)
|
||||
{
|
||||
void AyuGramSettings::set_voiceConfirmation(bool val) {
|
||||
voiceConfirmation = val;
|
||||
}
|
||||
|
||||
bool get_ghostModeEnabled()
|
||||
{
|
||||
bool get_ghostModeEnabled() {
|
||||
return ghostModeEnabled.current();
|
||||
}
|
||||
|
||||
rpl::producer<QString> get_deletedMarkReactive()
|
||||
{
|
||||
rpl::producer<QString> get_deletedMarkReactive() {
|
||||
return deletedMarkReactive.value();
|
||||
}
|
||||
|
||||
rpl::producer<QString> get_editedMarkReactive()
|
||||
{
|
||||
rpl::producer<QString> get_editedMarkReactive() {
|
||||
return editedMarkReactive.value();
|
||||
}
|
||||
|
||||
rpl::producer<int> get_showPeerIdReactive()
|
||||
{
|
||||
rpl::producer<int> get_showPeerIdReactive() {
|
||||
return showPeerIdReactive.value();
|
||||
}
|
||||
|
||||
rpl::producer<bool> get_ghostModeEnabledReactive()
|
||||
{
|
||||
rpl::producer<bool> get_ghostModeEnabledReactive() {
|
||||
return ghostModeEnabled.value();
|
||||
}
|
||||
|
||||
|
||||
@@ -3,95 +3,19 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "lang_auto.h"
|
||||
#include "ayu/libs/json.hpp"
|
||||
#include "ayu/libs/json_ext.hpp"
|
||||
#include "ayu/ui/ayu_assets.h"
|
||||
#include "rpl/producer.h"
|
||||
|
||||
namespace AyuSettings
|
||||
{
|
||||
namespace AyuSettings {
|
||||
|
||||
class AyuGramSettings
|
||||
{
|
||||
public:
|
||||
AyuGramSettings()
|
||||
{
|
||||
// ~ Ghost essentials
|
||||
sendReadMessages = true;
|
||||
sendReadStories = true;
|
||||
sendOnlinePackets = true;
|
||||
sendUploadProgress = true;
|
||||
sendOfflinePacketAfterOnline = false;
|
||||
|
||||
markReadAfterSend = true;
|
||||
markReadAfterReaction = true;
|
||||
markReadAfterPoll = true;
|
||||
useScheduledMessages = false;
|
||||
|
||||
// ~ Message edits & deletion history
|
||||
saveDeletedMessages = false;
|
||||
saveMessagesHistory = true;
|
||||
|
||||
// ~ QoL toggles
|
||||
disableAds = true;
|
||||
disableStories = false;
|
||||
collapseSimilarChannels = true;
|
||||
hideSimilarChannels = false;
|
||||
|
||||
uploadSpeedBoost = false;
|
||||
|
||||
disableNotificationsDelay = false;
|
||||
localPremium = false;
|
||||
copyUsernameAsLink = true;
|
||||
|
||||
// ~ Customization
|
||||
appIcon = AyuAssets::DEFAULT_ICON;
|
||||
simpleQuotesAndReplies = true;
|
||||
deletedMark = "🧹";
|
||||
editedMark = tr::lng_edited(tr::now);
|
||||
recentStickersCount = 50;
|
||||
|
||||
// context menu items
|
||||
// 0 - hide
|
||||
// 1 - show normally
|
||||
// 2 - show with SHIFT or CTRL pressed
|
||||
showReactionsPanelInContextMenu = 1;
|
||||
showViewsPanelInContextMenu = 1;
|
||||
showHideMessageInContextMenu = 0;
|
||||
showUserMessagesInContextMenu = 2;
|
||||
showMessageDetailsInContextMenu = 2;
|
||||
|
||||
showLReadToggleInDrawer = true;
|
||||
showSReadToggleInDrawer = true;
|
||||
showGhostToggleInDrawer = true;
|
||||
showStreamerToggleInDrawer = false;
|
||||
|
||||
showGhostToggleInTray = true;
|
||||
showStreamerToggleInTray = false;
|
||||
|
||||
mainFont = "";
|
||||
monoFont = "";
|
||||
|
||||
hideNotificationCounters = false;
|
||||
hideAllChatsFolder = false;
|
||||
|
||||
/*
|
||||
* showPeerId = 0 means no ID shown
|
||||
* showPeerId = 1 means ID shown as for Telegram API devs
|
||||
* showPeerId = 2 means ID shown as for Bot API devs (-100)
|
||||
*/
|
||||
showPeerId = 2;
|
||||
showMessageSeconds = false;
|
||||
|
||||
// ~ Confirmations
|
||||
stickerConfirmation = false;
|
||||
gifConfirmation = false;
|
||||
voiceConfirmation = false;
|
||||
}
|
||||
AyuGramSettings();
|
||||
|
||||
bool sendReadMessages;
|
||||
bool sendReadStories;
|
||||
@@ -146,12 +70,12 @@ public:
|
||||
|
||||
int showPeerId;
|
||||
bool showMessageSeconds;
|
||||
bool showMessageShot;
|
||||
|
||||
bool stickerConfirmation;
|
||||
bool gifConfirmation;
|
||||
bool voiceConfirmation;
|
||||
|
||||
public:
|
||||
void set_sendReadMessages(bool val);
|
||||
void set_sendReadStories(bool val);
|
||||
void set_sendOnlinePackets(bool val);
|
||||
@@ -207,6 +131,7 @@ public:
|
||||
|
||||
void set_showPeerId(int val);
|
||||
void set_showMessageSeconds(bool val);
|
||||
void set_showMessageShot(bool val);
|
||||
|
||||
void set_stickerConfirmation(bool val);
|
||||
void set_gifConfirmation(bool val);
|
||||
@@ -256,6 +181,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(
|
||||
hideAllChatsFolder,
|
||||
showPeerId,
|
||||
showMessageSeconds,
|
||||
showMessageShot,
|
||||
stickerConfirmation,
|
||||
gifConfirmation,
|
||||
voiceConfirmation
|
||||
|
||||
@@ -3,35 +3,30 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_state.h"
|
||||
|
||||
namespace AyuState
|
||||
{
|
||||
namespace AyuState {
|
||||
|
||||
std::unordered_map<PeerId, std::unordered_set<MsgId>> hiddenMessages;
|
||||
|
||||
void hide(PeerId peerId, MsgId messageId)
|
||||
{
|
||||
void hide(PeerId peerId, MsgId messageId) {
|
||||
hiddenMessages[peerId].insert(messageId);
|
||||
}
|
||||
|
||||
void hide(not_null<HistoryItem *> item)
|
||||
{
|
||||
void hide(not_null<HistoryItem*> item) {
|
||||
hide(item->history()->peer->id, item->id);
|
||||
}
|
||||
|
||||
bool isHidden(PeerId peerId, MsgId messageId)
|
||||
{
|
||||
auto it = hiddenMessages.find(peerId);
|
||||
bool isHidden(PeerId peerId, MsgId messageId) {
|
||||
const auto it = hiddenMessages.find(peerId);
|
||||
if (it != hiddenMessages.end()) {
|
||||
return it->second.find(messageId) != it->second.end();
|
||||
return it->second.contains(messageId);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isHidden(not_null<HistoryItem *> item)
|
||||
{
|
||||
bool isHidden(not_null<HistoryItem*> item) {
|
||||
return isHidden(item->history()->peer->id, item->id);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,20 +3,17 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "ayu_settings.h"
|
||||
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
||||
namespace AyuState
|
||||
{
|
||||
namespace AyuState {
|
||||
|
||||
void hide(PeerId peerId, MsgId messageId);
|
||||
void hide(not_null<HistoryItem *> item);
|
||||
void hide(not_null<HistoryItem*> item);
|
||||
bool isHidden(PeerId peerId, MsgId messageId);
|
||||
bool isHidden(not_null<HistoryItem *> item);
|
||||
bool isHidden(not_null<HistoryItem*> item);
|
||||
|
||||
}
|
||||
|
||||
@@ -3,28 +3,30 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu/ayu_url_handlers.h"
|
||||
|
||||
#include "base/qthelp_url.h"
|
||||
|
||||
#include "lang_auto.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "core/application.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
|
||||
namespace AyuUrlHandlers
|
||||
{
|
||||
#include <QDesktopServices>
|
||||
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace AyuUrlHandlers {
|
||||
|
||||
bool ResolveUser(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context)
|
||||
{
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
@@ -41,16 +43,19 @@ bool ResolveUser(
|
||||
return true;
|
||||
}
|
||||
|
||||
searchById(userId, &controller->session(), false, [=](const QString &title, UserData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
return;
|
||||
}
|
||||
searchById(userId,
|
||||
&controller->session(),
|
||||
false,
|
||||
[=](const QString &title, UserData *data)
|
||||
{
|
||||
if (data) {
|
||||
controller->showPeerInfo(data);
|
||||
return;
|
||||
}
|
||||
|
||||
Core::App().hideMediaView();
|
||||
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
|
||||
});
|
||||
Core::App().hideMediaView();
|
||||
Ui::show(Ui::MakeInformBox(tr::ayu_UserNotFoundMessage()));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -58,8 +63,7 @@ bool ResolveUser(
|
||||
bool HandleAyu(
|
||||
Window::SessionController *controller,
|
||||
const Match &match,
|
||||
const QVariant &context)
|
||||
{
|
||||
const QVariant &context) {
|
||||
if (!controller) {
|
||||
return false;
|
||||
}
|
||||
@@ -67,4 +71,36 @@ bool HandleAyu(
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
bool TryHandleSpotify(const QString &url) {
|
||||
if (!url.contains("spotify.com")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// docs on their url scheme
|
||||
// https://www.iana.org/assignments/uri-schemes/prov/spotify
|
||||
|
||||
using namespace qthelp;
|
||||
const auto matchOptions = RegExOption::CaseInsensitive;
|
||||
// https://regex101.com/r/l4Ogzf/2
|
||||
const auto match = regex_match(
|
||||
u"^(https?:\\/\\/)?([a-zA-Z0-9_]+)\\.spotify\\.com\\/(?<type>track|album|artist|user|playlist)\\/(?<identifier>[a-zA-Z0-9_\\/]+?)((\\?si=.+)?)$"_q,
|
||||
url,
|
||||
matchOptions);
|
||||
if (match) {
|
||||
const auto type = match->captured("type").toLower();
|
||||
const auto identifier = match->captured("identifier").replace("/", ":");
|
||||
|
||||
// '/' -> ':' for links like:
|
||||
// https://open.spotify.com/user/1185903410/playlist/6YAnJeVC7tgOiocOG23Dd
|
||||
// so it'll look like
|
||||
// spotify:user:1185903410:playlist:6YAnJeVC7tgOiocOG23Dd
|
||||
|
||||
const auto res = QString("spotify:%1:%2").arg(type).arg(identifier);
|
||||
|
||||
return QDesktopServices::openUrl(QUrl(res));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "window/window_session_controller.h"
|
||||
#include "base/qthelp_regex.h"
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
namespace AyuUrlHandlers
|
||||
{
|
||||
namespace AyuUrlHandlers {
|
||||
|
||||
using Match = qthelp::RegularExpressionMatch;
|
||||
|
||||
@@ -24,4 +23,6 @@ bool HandleAyu(
|
||||
const Match &match,
|
||||
const QVariant &context);
|
||||
|
||||
}
|
||||
bool TryHandleSpotify(const QString &url);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_worker.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "ayu_settings.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "core/application.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/entities.h"
|
||||
#include "main/main_account.h"
|
||||
#include "main/main_domain.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace AyuWorker {
|
||||
|
||||
std::unordered_map<ID, bool> state;
|
||||
|
||||
void markAsOnline(not_null<Main::Session*> session) {
|
||||
state[session->userId().bare] = true;
|
||||
}
|
||||
|
||||
void lateInit() {
|
||||
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
||||
if (const auto session = account->maybeSession()) {
|
||||
const auto id = session->userId().bare;
|
||||
state[id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void runOnce() {
|
||||
if (!Core::App().domain().started()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.empty()) {
|
||||
lateInit();
|
||||
}
|
||||
|
||||
const auto settings = &AyuSettings::getInstance();
|
||||
if (!settings->sendOfflinePacketAfterOnline) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto t = base::unixtime::now();
|
||||
const auto invalidateAll = cOtherOnline() >= t;
|
||||
|
||||
for (const auto &[index, account] : Core::App().domain().accounts()) {
|
||||
if (const auto session = account->maybeSession()) {
|
||||
const auto id = session->userId().bare;
|
||||
if (!state.contains(id)) {
|
||||
state[id] = true; // newly added account, I suppose
|
||||
}
|
||||
|
||||
if (invalidateAll || state[id] || session->user()->lastseen().isOnline(t)) {
|
||||
session->api().request(MTPaccount_UpdateStatus(
|
||||
MTP_bool(true)
|
||||
)).send();
|
||||
state[id] = false;
|
||||
|
||||
LOG(("[AyuGram] Sent offline for account with uid %1, invalidate %2").arg(id).arg(invalidateAll));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] void loop() {
|
||||
while (true) {
|
||||
runOnce();
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
std::thread t(loop);
|
||||
t.detach();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "window/window_session_controller.h"
|
||||
|
||||
namespace AyuWorker {
|
||||
|
||||
void markAsOnline(not_null<Main::Session*> session);
|
||||
void initialize();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "ayu_database.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "entities.h"
|
||||
#include "ayu/libs/sqlite/sqlite_orm.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
|
||||
using namespace sqlite_orm;
|
||||
auto storage = make_storage(
|
||||
"./tdata/ayudata.db",
|
||||
make_table(
|
||||
"DeletedMessage",
|
||||
make_column("fakeId", &DeletedMessage::fakeId, primary_key().autoincrement()),
|
||||
make_column("userId", &DeletedMessage::userId),
|
||||
make_column("dialogId", &DeletedMessage::dialogId),
|
||||
make_column("groupedId", &DeletedMessage::groupedId),
|
||||
make_column("peerId", &DeletedMessage::peerId),
|
||||
make_column("fromId", &DeletedMessage::fromId),
|
||||
make_column("topicId", &DeletedMessage::topicId),
|
||||
make_column("messageId", &DeletedMessage::messageId),
|
||||
make_column("date", &DeletedMessage::date),
|
||||
make_column("flags", &DeletedMessage::flags),
|
||||
make_column("editDate", &DeletedMessage::editDate),
|
||||
make_column("views", &DeletedMessage::views),
|
||||
make_column("fwdFlags", &DeletedMessage::fwdFlags),
|
||||
make_column("fwdFromId", &DeletedMessage::fwdFromId),
|
||||
make_column("fwdName", &DeletedMessage::fwdName),
|
||||
make_column("fwdDate", &DeletedMessage::fwdDate),
|
||||
make_column("fwdPostAuthor", &DeletedMessage::fwdPostAuthor),
|
||||
make_column("replyFlags", &DeletedMessage::replyFlags),
|
||||
make_column("replyMessageId", &DeletedMessage::replyMessageId),
|
||||
make_column("replyPeerId", &DeletedMessage::replyPeerId),
|
||||
make_column("replyTopId", &DeletedMessage::replyTopId),
|
||||
make_column("replyForumTopic", &DeletedMessage::replyForumTopic),
|
||||
make_column("replySerialized", &DeletedMessage::replySerialized),
|
||||
make_column("entityCreateDate", &DeletedMessage::entityCreateDate),
|
||||
make_column("text", &DeletedMessage::text),
|
||||
make_column("textEntities", &DeletedMessage::textEntities),
|
||||
make_column("mediaPath", &DeletedMessage::mediaPath),
|
||||
make_column("hqThumbPath", &DeletedMessage::hqThumbPath),
|
||||
make_column("documentType", &DeletedMessage::documentType),
|
||||
make_column("documentSerialized", &DeletedMessage::documentSerialized),
|
||||
make_column("thumbsSerialized", &DeletedMessage::thumbsSerialized),
|
||||
make_column("documentAttributesSerialized", &DeletedMessage::documentAttributesSerialized),
|
||||
make_column("mimeType", &DeletedMessage::mimeType)
|
||||
),
|
||||
make_table(
|
||||
"EditedMessage",
|
||||
make_column("fakeId", &EditedMessage::fakeId, primary_key().autoincrement()),
|
||||
make_column("userId", &EditedMessage::userId),
|
||||
make_column("dialogId", &EditedMessage::dialogId),
|
||||
make_column("groupedId", &EditedMessage::groupedId),
|
||||
make_column("peerId", &EditedMessage::peerId),
|
||||
make_column("fromId", &EditedMessage::fromId),
|
||||
make_column("topicId", &EditedMessage::topicId),
|
||||
make_column("messageId", &EditedMessage::messageId),
|
||||
make_column("date", &EditedMessage::date),
|
||||
make_column("flags", &EditedMessage::flags),
|
||||
make_column("editDate", &EditedMessage::editDate),
|
||||
make_column("views", &EditedMessage::views),
|
||||
make_column("fwdFlags", &EditedMessage::fwdFlags),
|
||||
make_column("fwdFromId", &EditedMessage::fwdFromId),
|
||||
make_column("fwdName", &EditedMessage::fwdName),
|
||||
make_column("fwdDate", &EditedMessage::fwdDate),
|
||||
make_column("fwdPostAuthor", &EditedMessage::fwdPostAuthor),
|
||||
make_column("replyFlags", &EditedMessage::replyFlags),
|
||||
make_column("replyMessageId", &EditedMessage::replyMessageId),
|
||||
make_column("replyPeerId", &EditedMessage::replyPeerId),
|
||||
make_column("replyTopId", &EditedMessage::replyTopId),
|
||||
make_column("replyForumTopic", &EditedMessage::replyForumTopic),
|
||||
make_column("replySerialized", &EditedMessage::replySerialized),
|
||||
make_column("entityCreateDate", &EditedMessage::entityCreateDate),
|
||||
make_column("text", &EditedMessage::text),
|
||||
make_column("textEntities", &EditedMessage::textEntities),
|
||||
make_column("mediaPath", &EditedMessage::mediaPath),
|
||||
make_column("hqThumbPath", &EditedMessage::hqThumbPath),
|
||||
make_column("documentType", &EditedMessage::documentType),
|
||||
make_column("documentSerialized", &EditedMessage::documentSerialized),
|
||||
make_column("thumbsSerialized", &EditedMessage::thumbsSerialized),
|
||||
make_column("documentAttributesSerialized", &EditedMessage::documentAttributesSerialized),
|
||||
make_column("mimeType", &EditedMessage::mimeType)
|
||||
),
|
||||
make_table(
|
||||
"DeletedDialog",
|
||||
make_column("fakeId", &DeletedDialog::fakeId, primary_key().autoincrement()),
|
||||
make_column("userId", &DeletedDialog::userId),
|
||||
make_column("dialogId", &DeletedDialog::dialogId),
|
||||
make_column("peerId", &DeletedDialog::peerId),
|
||||
make_column("folderId", &DeletedDialog::folderId),
|
||||
make_column("topMessage", &DeletedDialog::topMessage),
|
||||
make_column("lastMessageDate", &DeletedDialog::lastMessageDate),
|
||||
make_column("flags", &DeletedDialog::flags),
|
||||
make_column("entityCreateDate", &DeletedDialog::entityCreateDate)
|
||||
),
|
||||
make_table(
|
||||
"RegexFilter",
|
||||
make_column("id", &RegexFilter::id),
|
||||
make_column("text", &RegexFilter::text),
|
||||
make_column("enabled", &RegexFilter::enabled),
|
||||
make_column("caseInsensitive", &RegexFilter::caseInsensitive),
|
||||
make_column("dialogId", &RegexFilter::dialogId)
|
||||
),
|
||||
make_table(
|
||||
"RegexFilterGlobalExclusion",
|
||||
make_column("fakeId", &RegexFilterGlobalExclusion::fakeId, primary_key().autoincrement()),
|
||||
make_column("dialogId", &RegexFilterGlobalExclusion::dialogId),
|
||||
make_column("filterId", &RegexFilterGlobalExclusion::filterId)
|
||||
),
|
||||
make_table(
|
||||
"SpyMessageRead",
|
||||
make_column("fakeId", &SpyMessageRead::fakeId, primary_key().autoincrement()),
|
||||
make_column("userId", &SpyMessageRead::userId),
|
||||
make_column("dialogId", &SpyMessageRead::dialogId),
|
||||
make_column("messageId", &SpyMessageRead::messageId),
|
||||
make_column("entityCreateDate", &SpyMessageRead::entityCreateDate)
|
||||
),
|
||||
make_table(
|
||||
"SpyMessageContentsRead",
|
||||
make_column("fakeId", &SpyMessageContentsRead::fakeId, primary_key().autoincrement()),
|
||||
make_column("userId", &SpyMessageContentsRead::userId),
|
||||
make_column("dialogId", &SpyMessageContentsRead::dialogId),
|
||||
make_column("messageId", &SpyMessageContentsRead::messageId),
|
||||
make_column("entityCreateDate", &SpyMessageContentsRead::entityCreateDate)
|
||||
)
|
||||
);
|
||||
|
||||
namespace AyuDatabase {
|
||||
|
||||
void moveCurrentDatabase() {
|
||||
auto time = base::unixtime::now();
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db", QString("./tdata/ayudata_%1.db").arg(time).toStdString());
|
||||
}
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db-shm")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db-shm",
|
||||
QString("./tdata/ayudata_%1.db-shm").arg(time).toStdString());
|
||||
}
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db-wal")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db-wal",
|
||||
QString("./tdata/ayudata_%1.db-wal").arg(time).toStdString());
|
||||
}
|
||||
}
|
||||
|
||||
void initialize() {
|
||||
const auto res = storage.sync_schema_simulate(true);
|
||||
auto movePrevious = false;
|
||||
for (const auto val : res | std::views::values) {
|
||||
if (val == sync_schema_result::dropped_and_recreated) {
|
||||
movePrevious = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (movePrevious) {
|
||||
moveCurrentDatabase();
|
||||
}
|
||||
|
||||
try {
|
||||
storage.sync_schema(true);
|
||||
} catch (...) {
|
||||
LOG(("Failed to sync database schema"));
|
||||
LOG(("Moving current database just in case"));
|
||||
|
||||
moveCurrentDatabase();
|
||||
|
||||
storage.sync_schema();
|
||||
}
|
||||
|
||||
storage.begin_transaction();
|
||||
storage.commit();
|
||||
}
|
||||
|
||||
void addEditedMessage(const EditedMessage &message) {
|
||||
storage.begin_transaction();
|
||||
storage.insert(message);
|
||||
storage.commit();
|
||||
}
|
||||
|
||||
std::vector<EditedMessage> getEditedMessages(ID userId, ID dialogId, ID messageId) {
|
||||
return storage.get_all<EditedMessage>(
|
||||
where(
|
||||
c(&EditedMessage::userId) == userId and
|
||||
c(&EditedMessage::dialogId) == dialogId and
|
||||
c(&EditedMessage::messageId) == messageId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool hasRevisions(ID userId, ID dialogId, ID messageId) {
|
||||
return storage.count<EditedMessage>(
|
||||
where(
|
||||
c(&EditedMessage::userId) == userId and
|
||||
c(&EditedMessage::dialogId) == dialogId and
|
||||
c(&EditedMessage::messageId) == messageId
|
||||
)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,15 +3,12 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "entities.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
||||
namespace AyuDatabase
|
||||
{
|
||||
namespace AyuDatabase {
|
||||
|
||||
void initialize();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
ID replyPeerId;
|
||||
int replyTopId;
|
||||
bool replyForumTopic;
|
||||
std::vector<char> replySerialized;
|
||||
int entityCreateDate;
|
||||
std::string text;
|
||||
std::vector<char> textEntities;
|
||||
@@ -49,6 +50,7 @@ public:
|
||||
};
|
||||
|
||||
using DeletedMessage = AyuMessageBase<struct DeletedMessageTag>;
|
||||
|
||||
using EditedMessage = AyuMessageBase<struct EditedMessageTag>;
|
||||
|
||||
class DeletedDialog
|
||||
@@ -58,8 +60,47 @@ public:
|
||||
ID userId;
|
||||
ID dialogId;
|
||||
ID peerId;
|
||||
std::unique_ptr<int> folderId; // nullable
|
||||
int topMessage;
|
||||
int lastMessageDate;
|
||||
int flags;
|
||||
int entityCreateDate;
|
||||
};
|
||||
|
||||
class RegexFilter
|
||||
{
|
||||
public:
|
||||
std::vector<char> id;
|
||||
std::string text;
|
||||
bool enabled;
|
||||
bool caseInsensitive;
|
||||
std::unique_ptr<ID> dialogId; // nullable
|
||||
};
|
||||
|
||||
class RegexFilterGlobalExclusion
|
||||
{
|
||||
public:
|
||||
ID fakeId;
|
||||
ID dialogId;
|
||||
std::vector<char> filterId;
|
||||
};
|
||||
|
||||
class SpyMessageRead
|
||||
{
|
||||
public:
|
||||
ID fakeId;
|
||||
ID userId;
|
||||
ID dialogId;
|
||||
int messageId;
|
||||
int entityCreateDate;
|
||||
};
|
||||
|
||||
class SpyMessageContentsRead
|
||||
{
|
||||
public:
|
||||
ID fakeId;
|
||||
ID userId;
|
||||
ID dialogId;
|
||||
int messageId;
|
||||
int entityCreateDate;
|
||||
};
|
||||
@@ -3,11 +3,11 @@
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
#include "ayu_messages_controller.h"
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "messages_storage.h"
|
||||
|
||||
#include "ayu/ayu_constants.h"
|
||||
#include "ayu/database/ayu_database.h"
|
||||
#include "ayu/data/ayu_database.h"
|
||||
#include "ayu/utils/ayu_mapper.h"
|
||||
#include "ayu/utils/telegram_helpers.h"
|
||||
|
||||
@@ -22,28 +22,9 @@
|
||||
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace AyuMessages
|
||||
{
|
||||
namespace AyuMessages {
|
||||
|
||||
std::optional<ayu_messages_controller> controller = std::nullopt;
|
||||
|
||||
void initialize()
|
||||
{
|
||||
if (controller.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
controller = ayu_messages_controller();
|
||||
}
|
||||
|
||||
ayu_messages_controller &getInstance()
|
||||
{
|
||||
initialize();
|
||||
return controller.value();
|
||||
}
|
||||
|
||||
void map(HistoryMessageEdition &edition, not_null<HistoryItem *> item, EditedMessage &message)
|
||||
{
|
||||
void map(HistoryMessageEdition &edition, not_null<HistoryItem*> item, EditedMessage &message) {
|
||||
message.userId = item->history()->owner().session().userId().bare;
|
||||
message.dialogId = getDialogIdFromPeer(item->history()->peer);
|
||||
message.groupedId = item->groupId().value;
|
||||
@@ -58,19 +39,18 @@ void map(HistoryMessageEdition &edition, not_null<HistoryItem *> item, EditedMes
|
||||
|
||||
if (auto edited = item->Get<HistoryMessageEdited>()) {
|
||||
message.editDate = edited->date;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
message.editDate = base::unixtime::now();
|
||||
}
|
||||
|
||||
message.views = item->viewsCount();
|
||||
message.entityCreateDate = base::unixtime::now(); // todo: rework
|
||||
|
||||
auto serializedText = serializeTextWithEntities(item);
|
||||
auto serializedText = AyuMapper::serializeTextWithEntities(item);
|
||||
message.text = serializedText.first;
|
||||
// message.textEntities = serializedText.second;
|
||||
message.textEntities = serializedText.second;
|
||||
|
||||
// todo:
|
||||
// todo: implement mapping
|
||||
message.mediaPath = "/";
|
||||
message.documentType = DOCUMENT_TYPE_NONE;
|
||||
|
||||
@@ -80,16 +60,14 @@ void map(HistoryMessageEdition &edition, not_null<HistoryItem *> item, EditedMes
|
||||
// message.mimeType;
|
||||
}
|
||||
|
||||
void ayu_messages_controller::addEditedMessage(HistoryMessageEdition &edition, not_null<HistoryItem *> item)
|
||||
{
|
||||
void addEditedMessage(HistoryMessageEdition &edition, not_null<HistoryItem*> item) {
|
||||
EditedMessage message;
|
||||
map(edition, item, message);
|
||||
|
||||
AyuDatabase::addEditedMessage(message);
|
||||
}
|
||||
|
||||
std::vector<EditedMessage> ayu_messages_controller::getEditedMessages(HistoryItem *item)
|
||||
{
|
||||
std::vector<EditedMessage> getEditedMessages(not_null<HistoryItem*> item) {
|
||||
auto userId = item->history()->owner().session().userId().bare;
|
||||
auto dialogId = getDialogIdFromPeer(item->history()->peer);
|
||||
auto msgId = item->id.bare;
|
||||
@@ -97,8 +75,7 @@ std::vector<EditedMessage> ayu_messages_controller::getEditedMessages(HistoryIte
|
||||
return AyuDatabase::getEditedMessages(userId, dialogId, msgId);
|
||||
}
|
||||
|
||||
bool ayu_messages_controller::hasRevisions(not_null<HistoryItem *> item)
|
||||
{
|
||||
bool hasRevisions(not_null<HistoryItem*> item) {
|
||||
auto userId = item->history()->owner().session().userId().bare;
|
||||
auto dialogId = getDialogIdFromPeer(item->history()->peer);
|
||||
auto msgId = item->id.bare;
|
||||
@@ -0,0 +1,19 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "entities.h"
|
||||
|
||||
#include "history/history_item_edition.h"
|
||||
|
||||
namespace AyuMessages {
|
||||
|
||||
void addEditedMessage(HistoryMessageEdition &edition, not_null<HistoryItem*> item);
|
||||
std::vector<EditedMessage> getEditedMessages(not_null<HistoryItem*> item);
|
||||
bool hasRevisions(not_null<HistoryItem*> item);
|
||||
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2023
|
||||
#include "ayu_database.h"
|
||||
|
||||
#include "entities.h"
|
||||
#include "ayu/libs/sqlite/sqlite_orm.h"
|
||||
|
||||
#include "base/unixtime.h"
|
||||
|
||||
using namespace sqlite_orm;
|
||||
|
||||
auto storage = make_storage("./tdata/ayudata.db",
|
||||
make_table("DeletedMessage",
|
||||
make_column("userId", &DeletedMessage::userId),
|
||||
make_column("dialogId", &DeletedMessage::dialogId),
|
||||
make_column("groupedId", &DeletedMessage::groupedId),
|
||||
make_column("peerId", &DeletedMessage::peerId),
|
||||
make_column("fromId", &DeletedMessage::fromId),
|
||||
make_column("topicId", &DeletedMessage::topicId),
|
||||
make_column("messageId", &DeletedMessage::messageId),
|
||||
make_column("date", &DeletedMessage::date),
|
||||
make_column("flags", &DeletedMessage::flags),
|
||||
make_column("editDate", &DeletedMessage::editDate),
|
||||
make_column("views", &DeletedMessage::views),
|
||||
make_column("fwdFlags", &DeletedMessage::fwdFlags),
|
||||
make_column("fwdFromId", &DeletedMessage::fwdFromId),
|
||||
make_column("fwdName", &DeletedMessage::fwdName),
|
||||
make_column("fwdDate", &DeletedMessage::fwdDate),
|
||||
make_column("fwdPostAuthor", &DeletedMessage::fwdPostAuthor),
|
||||
make_column("replyFlags", &DeletedMessage::replyFlags),
|
||||
make_column("replyMessageId", &DeletedMessage::replyMessageId),
|
||||
make_column("replyPeerId", &DeletedMessage::replyPeerId),
|
||||
make_column("replyTopId", &DeletedMessage::replyTopId),
|
||||
make_column("replyForumTopic", &DeletedMessage::replyForumTopic),
|
||||
make_column("entityCreateDate", &DeletedMessage::entityCreateDate),
|
||||
make_column("text", &DeletedMessage::text),
|
||||
make_column("textEntities", &DeletedMessage::textEntities),
|
||||
make_column("mediaPath", &DeletedMessage::mediaPath),
|
||||
make_column("hqThumbPath", &DeletedMessage::hqThumbPath),
|
||||
make_column("documentType", &DeletedMessage::documentType),
|
||||
make_column("documentSerialized", &DeletedMessage::documentSerialized),
|
||||
make_column("thumbsSerialized", &DeletedMessage::thumbsSerialized),
|
||||
make_column("documentAttributesSerialized",
|
||||
&DeletedMessage::documentAttributesSerialized),
|
||||
make_column("mimeType", &DeletedMessage::mimeType)
|
||||
),
|
||||
make_table("EditedMessage",
|
||||
make_column("userId", &EditedMessage::userId),
|
||||
make_column("dialogId", &EditedMessage::dialogId),
|
||||
make_column("groupedId", &EditedMessage::groupedId),
|
||||
make_column("peerId", &EditedMessage::peerId),
|
||||
make_column("fromId", &EditedMessage::fromId),
|
||||
make_column("topicId", &EditedMessage::topicId),
|
||||
make_column("messageId", &EditedMessage::messageId),
|
||||
make_column("date", &EditedMessage::date),
|
||||
make_column("flags", &EditedMessage::flags),
|
||||
make_column("editDate", &EditedMessage::editDate),
|
||||
make_column("views", &EditedMessage::views),
|
||||
make_column("fwdFlags", &EditedMessage::fwdFlags),
|
||||
make_column("fwdFromId", &EditedMessage::fwdFromId),
|
||||
make_column("fwdName", &EditedMessage::fwdName),
|
||||
make_column("fwdDate", &EditedMessage::fwdDate),
|
||||
make_column("fwdPostAuthor", &EditedMessage::fwdPostAuthor),
|
||||
make_column("replyFlags", &EditedMessage::replyFlags),
|
||||
make_column("replyMessageId", &EditedMessage::replyMessageId),
|
||||
make_column("replyPeerId", &EditedMessage::replyPeerId),
|
||||
make_column("replyTopId", &EditedMessage::replyTopId),
|
||||
make_column("replyForumTopic", &EditedMessage::replyForumTopic),
|
||||
make_column("entityCreateDate", &EditedMessage::entityCreateDate),
|
||||
make_column("text", &EditedMessage::text),
|
||||
make_column("textEntities", &EditedMessage::textEntities),
|
||||
make_column("mediaPath", &EditedMessage::mediaPath),
|
||||
make_column("hqThumbPath", &EditedMessage::hqThumbPath),
|
||||
make_column("documentType", &EditedMessage::documentType),
|
||||
make_column("documentSerialized", &EditedMessage::documentSerialized),
|
||||
make_column("thumbsSerialized", &EditedMessage::thumbsSerialized),
|
||||
make_column("documentAttributesSerialized",
|
||||
&EditedMessage::documentAttributesSerialized),
|
||||
make_column("mimeType", &EditedMessage::mimeType)
|
||||
),
|
||||
make_table("DeletedDialog",
|
||||
make_column("userId", &DeletedDialog::userId),
|
||||
make_column("dialogId", &DeletedDialog::dialogId),
|
||||
make_column("peerId", &DeletedDialog::peerId),
|
||||
make_column("topMessage", &DeletedDialog::topMessage),
|
||||
make_column("lastMessageDate", &DeletedDialog::lastMessageDate),
|
||||
make_column("flags", &DeletedDialog::flags),
|
||||
make_column("entityCreateDate", &DeletedDialog::entityCreateDate)
|
||||
)
|
||||
);
|
||||
|
||||
namespace AyuDatabase
|
||||
{
|
||||
|
||||
void initialize()
|
||||
{
|
||||
// move to `tdata` from legacy version
|
||||
if (std::filesystem::exists("ayugram.db")) {
|
||||
try {
|
||||
std::filesystem::rename("ayugram.db", "./tdata/ayudata.db");
|
||||
}
|
||||
catch (std::filesystem::filesystem_error &e) {
|
||||
LOG(("Failed to move database: %1").arg(e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
storage.sync_schema();
|
||||
}
|
||||
catch (...) {
|
||||
auto time = base::unixtime::now();
|
||||
|
||||
LOG(("Failed to sync database schema"));
|
||||
LOG(("Moving current database just in case"));
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db", QString("./tdata/ayudata_%1.db").arg(time).toStdString());
|
||||
}
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db-shm")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db-shm",
|
||||
QString("./tdata/ayudata_%1.db-shm").arg(time).toStdString());
|
||||
}
|
||||
|
||||
if (std::filesystem::exists("./tdata/ayudata.db-wal")) {
|
||||
std::filesystem::rename("./tdata/ayudata.db-wal",
|
||||
QString("./tdata/ayudata_%1.db-wal").arg(time).toStdString());
|
||||
}
|
||||
|
||||
storage.sync_schema();
|
||||
}
|
||||
|
||||
storage.begin_transaction();
|
||||
storage.commit();
|
||||
}
|
||||
|
||||
void addEditedMessage(const EditedMessage &message)
|
||||
{
|
||||
storage.begin_transaction();
|
||||
storage.insert(message);
|
||||
storage.commit();
|
||||
}
|
||||
|
||||
std::vector<EditedMessage> getEditedMessages(ID userId, ID dialogId, ID messageId)
|
||||
{
|
||||
return storage.get_all<EditedMessage>(
|
||||
where(
|
||||
c(&EditedMessage::userId) == userId and
|
||||
c(&EditedMessage::dialogId) == dialogId and
|
||||
c(&EditedMessage::messageId) == messageId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
bool hasRevisions(ID userId, ID dialogId, ID messageId)
|
||||
{
|
||||
return storage.count<EditedMessage>(
|
||||
where(
|
||||
c(&EditedMessage::userId) == userId and
|
||||
c(&EditedMessage::dialogId) == dialogId and
|
||||
c(&EditedMessage::messageId) == messageId
|
||||
)
|
||||
) > 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,416 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#include "message_shot.h"
|
||||
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_ayu_styles.h"
|
||||
|
||||
#include "qguiapplication.h"
|
||||
#include "ayu/ui/boxes/message_shot_box.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/media/history_view_media.h"
|
||||
#include "main/main_session.h"
|
||||
#include "styles/style_chat.h"
|
||||
#include "ui/painter.h"
|
||||
#include "ui/chat/chat_theme.h"
|
||||
#include "ui/effects/path_shift_gradient.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
|
||||
namespace AyuFeatures::MessageShot {
|
||||
|
||||
ShotConfig *config;
|
||||
|
||||
Window::Theme::EmbeddedType defaultSelected = Window::Theme::EmbeddedType(-1);
|
||||
std::optional<QColor> defaultSelectedColor;
|
||||
|
||||
std::optional<Data::CloudTheme> customSelected;
|
||||
|
||||
rpl::event_stream<> resetDefaultSelectedStream;
|
||||
rpl::event_stream<> resetCustomSelectedStream;
|
||||
|
||||
bool takingShot = false;
|
||||
bool choosingTheme = false;
|
||||
|
||||
rpl::event_stream<Data::CloudTheme> themeChosenStream;
|
||||
rpl::event_stream<style::palette> paletteChosenStream;
|
||||
|
||||
void setShotConfig(ShotConfig &config) {
|
||||
MessageShot::config = &config;
|
||||
}
|
||||
|
||||
void resetShotConfig() {
|
||||
config = nullptr;
|
||||
}
|
||||
|
||||
ShotConfig getShotConfig() {
|
||||
return *config;
|
||||
}
|
||||
|
||||
void setDefaultSelected(const Window::Theme::EmbeddedType type) {
|
||||
resetCustomSelected();
|
||||
defaultSelected = type;
|
||||
}
|
||||
|
||||
Window::Theme::EmbeddedType getSelectedFromDefault() {
|
||||
return defaultSelected;
|
||||
}
|
||||
|
||||
void setDefaultSelectedColor(const QColor color) {
|
||||
resetCustomSelected();
|
||||
defaultSelectedColor = color;
|
||||
}
|
||||
|
||||
std::optional<QColor> getSelectedColorFromDefault() {
|
||||
return defaultSelectedColor;
|
||||
}
|
||||
|
||||
void setCustomSelected(const Data::CloudTheme theme) {
|
||||
resetDefaultSelected();
|
||||
customSelected = theme;
|
||||
}
|
||||
|
||||
std::optional<Data::CloudTheme> getSelectedFromCustom() {
|
||||
return customSelected;
|
||||
}
|
||||
|
||||
void resetDefaultSelected() {
|
||||
defaultSelected = Window::Theme::EmbeddedType(-1);
|
||||
resetDefaultSelectedStream.fire({});
|
||||
}
|
||||
|
||||
void resetCustomSelected() {
|
||||
customSelected = std::nullopt;
|
||||
resetCustomSelectedStream.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> resetDefaultSelectedEvents() {
|
||||
return resetDefaultSelectedStream.events();
|
||||
}
|
||||
|
||||
rpl::producer<> resetCustomSelectedEvents() {
|
||||
return resetCustomSelectedStream.events();
|
||||
}
|
||||
|
||||
bool ignoreRender(RenderPart part) {
|
||||
if (!config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto ignoreDate = !config->showDate;
|
||||
const auto ignoreReactions = !config->showReactions;
|
||||
|
||||
return isTakingShot() &&
|
||||
((part == RenderPart::Date && ignoreDate) ||
|
||||
(part == RenderPart::Reactions && ignoreReactions));
|
||||
}
|
||||
|
||||
bool isTakingShot() {
|
||||
return takingShot;
|
||||
}
|
||||
|
||||
bool setChoosingTheme(bool val) {
|
||||
choosingTheme = val;
|
||||
return choosingTheme;
|
||||
}
|
||||
|
||||
bool isChoosingTheme() {
|
||||
return choosingTheme;
|
||||
}
|
||||
|
||||
rpl::producer<Data::CloudTheme> themeChosen() {
|
||||
return themeChosenStream.events();
|
||||
}
|
||||
|
||||
void setTheme(Data::CloudTheme theme) {
|
||||
themeChosenStream.fire(std::move(theme));
|
||||
}
|
||||
|
||||
void setPalette(style::palette &palette) {
|
||||
paletteChosenStream.fire(std::move(palette));
|
||||
}
|
||||
|
||||
rpl::producer<style::palette> paletteChosen() {
|
||||
return paletteChosenStream.events();
|
||||
}
|
||||
|
||||
class MessageShotDelegate final : public HistoryView::DefaultElementDelegate
|
||||
{
|
||||
public:
|
||||
MessageShotDelegate(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Ui::ChatStyle*> st,
|
||||
Fn<void()> update);
|
||||
|
||||
bool elementAnimationsPaused() override;
|
||||
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
|
||||
HistoryView::Context elementContext() override;
|
||||
bool elementIsChatWide() override;
|
||||
|
||||
private:
|
||||
const not_null<QWidget*> _parent;
|
||||
const std::unique_ptr<Ui::PathShiftGradient> _pathGradient;
|
||||
};
|
||||
|
||||
MessageShotDelegate::MessageShotDelegate(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<Ui::ChatStyle*> st,
|
||||
Fn<void()> update)
|
||||
: _parent(parent)
|
||||
, _pathGradient(HistoryView::MakePathShiftGradient(st, update)) {
|
||||
}
|
||||
|
||||
bool MessageShotDelegate::elementAnimationsPaused() {
|
||||
return _parent->window()->isActiveWindow();
|
||||
}
|
||||
|
||||
auto MessageShotDelegate::elementPathShiftGradient()
|
||||
-> not_null<Ui::PathShiftGradient*> {
|
||||
return _pathGradient.get();
|
||||
}
|
||||
|
||||
HistoryView::Context MessageShotDelegate::elementContext() {
|
||||
return HistoryView::Context::AdminLog;
|
||||
}
|
||||
|
||||
bool MessageShotDelegate::elementIsChatWide() {
|
||||
return true;
|
||||
}
|
||||
|
||||
QImage removeEmptySpaceAround(const QImage &original) {
|
||||
if (original.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int minX = original.width();
|
||||
int minY = original.height();
|
||||
int maxX = 0;
|
||||
int maxY = 0;
|
||||
|
||||
for (int x = 0; x < original.width(); ++x) {
|
||||
for (int y = 0; y < original.height(); ++y) {
|
||||
if (qAlpha(original.pixel(x, y)) != 0) {
|
||||
minX = std::min(minX, x);
|
||||
minY = std::min(minY, y);
|
||||
maxX = std::max(maxX, x);
|
||||
maxY = std::max(maxY, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (minX > maxX || minY > maxY) {
|
||||
LOG(("Image is fully transparent ?"));
|
||||
return {};
|
||||
}
|
||||
|
||||
const QRect bounds(minX, minY, maxX - minX + 1, maxY - minY + 1);
|
||||
return original.copy(bounds);
|
||||
}
|
||||
|
||||
QImage addPadding(const QImage &original) {
|
||||
if (original.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
QImage paddedImage(
|
||||
original.width() + 2 * st::messageShotPadding,
|
||||
original.height() + 2 * st::messageShotPadding,
|
||||
QImage::Format_ARGB32_Premultiplied
|
||||
);
|
||||
paddedImage.fill(Qt::transparent);
|
||||
|
||||
Painter painter(&paddedImage);
|
||||
painter.drawImage(st::messageShotPadding, st::messageShotPadding, original);
|
||||
painter.end();
|
||||
|
||||
return paddedImage;
|
||||
}
|
||||
|
||||
QColor makeDefaultBackgroundColor() {
|
||||
if (Window::Theme::IsNightMode()) {
|
||||
return st::boxBg->c.lighter(175);
|
||||
}
|
||||
|
||||
return st::boxBg->c.darker(110);
|
||||
}
|
||||
|
||||
QImage Make(not_null<QWidget*> box, const ShotConfig &config) {
|
||||
const auto controller = config.controller;
|
||||
const auto st = config.st;
|
||||
auto messages = config.messages;
|
||||
|
||||
if (messages.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
takingShot = true;
|
||||
|
||||
auto delegate = std::make_unique<MessageShotDelegate>(box,
|
||||
st.get(),
|
||||
[=]
|
||||
{
|
||||
box->update();
|
||||
});
|
||||
|
||||
// remove deleted messages
|
||||
messages.erase(
|
||||
std::ranges::remove_if(
|
||||
messages,
|
||||
[=](const auto &message)
|
||||
{
|
||||
return !message || !controller->session().data().message(message->fullId());
|
||||
}).begin(),
|
||||
messages.end()
|
||||
);
|
||||
|
||||
if (messages.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unordered_map<not_null<HistoryItem*>, std::shared_ptr<HistoryView::Element>> createdViews;
|
||||
createdViews.reserve(messages.size());
|
||||
for (const auto &message : messages) {
|
||||
createdViews.emplace(message, message->createView(delegate.get()));
|
||||
}
|
||||
|
||||
auto getView = [=](not_null<HistoryItem*> msg)
|
||||
{
|
||||
return createdViews.at(msg).get();
|
||||
};
|
||||
|
||||
// recalculate blocks
|
||||
if (messages.size() > 1) {
|
||||
auto currentMsg = messages[0].get();
|
||||
|
||||
for (auto i = 1; i != messages.size(); ++i) {
|
||||
const auto nextMsg = messages[i].get();
|
||||
if (getView(nextMsg)->isHidden()) {
|
||||
getView(nextMsg)->setDisplayDate(false);
|
||||
} else {
|
||||
const auto viewDate = getView(currentMsg)->dateTime();
|
||||
const auto nextDate = getView(nextMsg)->dateTime();
|
||||
getView(nextMsg)->setDisplayDate(nextDate.date() != viewDate.date());
|
||||
auto attached = getView(nextMsg)->computeIsAttachToPrevious(getView(currentMsg));
|
||||
getView(nextMsg)->setAttachToPrevious(attached, getView(currentMsg));
|
||||
getView(currentMsg)->setAttachToNext(attached, getView(nextMsg));
|
||||
currentMsg = nextMsg;
|
||||
}
|
||||
}
|
||||
|
||||
getView(messages[messages.size() - 1])->setAttachToNext(false);
|
||||
} else {
|
||||
getView(messages[0])->setAttachToPrevious(false);
|
||||
getView(messages[0])->setAttachToNext(false);
|
||||
}
|
||||
|
||||
// calculate the size of the image
|
||||
int width = st::msgMaxWidth + (st::boxPadding.left() + st::boxPadding.right());
|
||||
int height = 0;
|
||||
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
const auto &message = messages[i];
|
||||
const auto view = getView(message);
|
||||
|
||||
view->itemDataChanged(); // refresh reactions
|
||||
height += view->resizeGetHeight(width);
|
||||
}
|
||||
|
||||
// width *= style::DevicePixelRatio();
|
||||
height *= style::DevicePixelRatio();
|
||||
|
||||
// create the image
|
||||
QImage image(width, height, QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::transparent);
|
||||
|
||||
const auto viewport = QRect(0, 0, width, height);
|
||||
|
||||
Painter p(&image);
|
||||
|
||||
// draw the messages
|
||||
int y = 0;
|
||||
for (int i = 0; i < messages.size(); i++) {
|
||||
const auto &message = messages[i];
|
||||
const auto view = getView(message);
|
||||
|
||||
const auto displayUserpic = view->displayFromPhoto() || message->isPost();
|
||||
|
||||
const auto rect = QRect(0, y, width, view->height());
|
||||
|
||||
auto context = controller->defaultChatTheme()->preparePaintContext(
|
||||
st.get(),
|
||||
viewport,
|
||||
rect,
|
||||
true);
|
||||
|
||||
p.translate(0, y);
|
||||
view->draw(p, context);
|
||||
p.translate(0, -y);
|
||||
|
||||
if (displayUserpic) {
|
||||
const auto picX = st::msgMargin.left();
|
||||
const auto picY = y + view->height() - st::msgPhotoSize;
|
||||
|
||||
auto userpicView =
|
||||
!message->displayFrom()->activeUserpicView().null()
|
||||
? message->displayFrom()->activeUserpicView()
|
||||
: message->displayFrom()->createUserpicView();
|
||||
message->displayFrom()->paintUserpic(p, userpicView, picX, picY, st::msgPhotoSize);
|
||||
}
|
||||
|
||||
y += view->height();
|
||||
}
|
||||
|
||||
takingShot = false;
|
||||
|
||||
auto result = addPadding(removeEmptySpaceAround(image));
|
||||
if (!config.showBackground) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto newResult = QImage(result.size(), QImage::Format_ARGB32_Premultiplied);
|
||||
newResult.fill(makeDefaultBackgroundColor());
|
||||
|
||||
Painter painter(&newResult);
|
||||
painter.drawImage(0, 0, result);
|
||||
|
||||
return newResult;
|
||||
}
|
||||
|
||||
void Wrapper(not_null<HistoryView::ListWidget*> widget) {
|
||||
const auto items = widget->getSelectedIds();
|
||||
if (items.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto session = &widget->session();
|
||||
const auto controller = widget->session().tryResolveWindow();
|
||||
if (!controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto messages = ranges::views::all(items)
|
||||
| ranges::views::transform([=](const auto item)
|
||||
{
|
||||
return gsl::not_null(session->data().message(item));
|
||||
})
|
||||
| ranges::to_vector;
|
||||
|
||||
const AyuFeatures::MessageShot::ShotConfig config = {
|
||||
controller,
|
||||
std::make_shared<Ui::ChatStyle>(controller->chatStyle()),
|
||||
messages,
|
||||
};
|
||||
auto box = Box<MessageShotBox>(config);
|
||||
Ui::show(std::move(box));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// This is the source code of AyuGram for Desktop.
|
||||
//
|
||||
// We do not and cannot prevent the use of our code,
|
||||
// but be respectful and credit the original author.
|
||||
//
|
||||
// Copyright @Radolyn, 2024
|
||||
#pragma once
|
||||
|
||||
#include "history/view/history_view_list_widget.h"
|
||||
#include "ui/chat/chat_style.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "window/themes/window_themes_embedded.h"
|
||||
|
||||
namespace AyuFeatures::MessageShot {
|
||||
|
||||
struct ShotConfig
|
||||
{
|
||||
not_null<Window::SessionController*> controller;
|
||||
std::shared_ptr<Ui::ChatStyle> st;
|
||||
std::vector<not_null<HistoryItem*>> messages;
|
||||
|
||||
bool showBackground;
|
||||
bool showDate;
|
||||
bool showReactions;
|
||||
};
|
||||
|
||||
enum RenderPart
|
||||
{
|
||||
Date,
|
||||
Reactions,
|
||||
};
|
||||
|
||||
void setShotConfig(ShotConfig &config);
|
||||
void resetShotConfig();
|
||||
ShotConfig getShotConfig();
|
||||
|
||||
// for default themes
|
||||
void setDefaultSelected(Window::Theme::EmbeddedType type);
|
||||
Window::Theme::EmbeddedType getSelectedFromDefault();
|
||||
|
||||
void setDefaultSelectedColor(QColor color);
|
||||
std::optional<QColor> getSelectedColorFromDefault();
|
||||
|
||||
// for custom themes
|
||||
void setCustomSelected(Data::CloudTheme theme);
|
||||
std::optional<Data::CloudTheme> getSelectedFromCustom();
|
||||
|
||||
// resets
|
||||
void resetDefaultSelected();
|
||||
void resetCustomSelected();
|
||||
|
||||
rpl::producer<> resetDefaultSelectedEvents();
|
||||
rpl::producer<> resetCustomSelectedEvents();
|
||||
|
||||
bool ignoreRender(RenderPart part);
|
||||
bool isTakingShot();
|
||||
|
||||
bool isChoosingTheme();
|
||||
bool setChoosingTheme(bool val);
|
||||
|
||||
void setTheme(Data::CloudTheme theme);
|
||||
rpl::producer<Data::CloudTheme> themeChosen();
|
||||
|
||||
void setPalette(style::palette &palette);
|
||||
rpl::producer<style::palette> paletteChosen();
|
||||
|
||||
// util
|
||||
QColor makeDefaultBackgroundColor();
|
||||
|
||||
QImage Make(not_null<QWidget*> box, const ShotConfig &config);
|
||||
|
||||
void Wrapper(not_null<HistoryView::ListWidget*> widget);
|
||||
|
||||
}
|
||||