Compare commits
342 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
534058fe9b | ||
|
|
6d62673e9e | ||
|
|
677dbd5d6e | ||
|
|
452440f50b | ||
|
|
5a7d8bcffb | ||
|
|
63e89ddc9a | ||
|
|
4e2c8bbc26 | ||
|
|
9bbcbd4bb3 | ||
|
|
273ac5eaf1 | ||
|
|
951db83ab6 | ||
|
|
a868c7bc8b | ||
|
|
a403ad7d37 | ||
|
|
90f5f7dded | ||
|
|
aef88559e8 | ||
|
|
6295d85ef2 | ||
|
|
a27ea2d631 | ||
|
|
de8de84a33 | ||
|
|
80bb6b65a7 | ||
|
|
f8963d7e4b | ||
|
|
c79d16a0d6 | ||
|
|
aa16bcd604 | ||
|
|
935232eaa2 | ||
|
|
e273695cc9 | ||
|
|
827784e3b2 | ||
|
|
b501af0b8f | ||
|
|
8f87cfe29d | ||
|
|
79398fe6cf | ||
|
|
355747d7bf | ||
|
|
a032f24d58 | ||
|
|
320105f201 | ||
|
|
54984efa0a | ||
|
|
d57f5460b7 | ||
|
|
5bc47e5203 | ||
|
|
f0a03223e8 | ||
|
|
007ab3b7b8 | ||
|
|
f813bb704f | ||
|
|
93809ec404 | ||
|
|
64e9958585 | ||
|
|
067138f1bf | ||
|
|
cfa88b840a | ||
|
|
32f955404b | ||
|
|
ff84962148 | ||
|
|
727f8aec13 | ||
|
|
d77afef8b0 | ||
|
|
775cede16f | ||
|
|
c6d3fd883a | ||
|
|
1473c14668 | ||
|
|
6764a3cc86 | ||
|
|
3845985a6b | ||
|
|
76a716007c | ||
|
|
993877b0d4 | ||
|
|
8b3d203861 | ||
|
|
85d8273009 | ||
|
|
f8e094392f | ||
|
|
46bafc2dcc | ||
|
|
e4ce08e64e | ||
|
|
aebdc2fd94 | ||
|
|
1d85c8a6b6 | ||
|
|
5eeb8143b6 | ||
|
|
68009b6fba | ||
|
|
62568daffe | ||
|
|
116e3fd9c5 | ||
|
|
76f951e3e6 | ||
|
|
1a273702d3 | ||
|
|
4678de0440 | ||
|
|
e1c68892d4 | ||
|
|
629c216a7f | ||
|
|
f3c8da4819 | ||
|
|
9ba482f56f | ||
|
|
2a59802a16 | ||
|
|
8668d43032 | ||
|
|
d6f7cae024 | ||
|
|
8391d43057 | ||
|
|
da77c10f60 | ||
|
|
301aa9572f | ||
|
|
675499df4d | ||
|
|
0a1165dac9 | ||
|
|
a495de7cf8 | ||
|
|
2161858088 | ||
|
|
001be82566 | ||
|
|
85b3d3f64d | ||
|
|
3bdce06e19 | ||
|
|
36fe4ff327 | ||
|
|
afd1548533 | ||
|
|
595af2c6d9 | ||
|
|
2bbf17b672 | ||
|
|
43570d1613 | ||
|
|
2432845df2 | ||
|
|
0bf854bf18 | ||
|
|
a0c8d522ef | ||
|
|
c10588a7dc | ||
|
|
13ab055fe0 | ||
|
|
2e972fb678 | ||
|
|
5b7059dccd | ||
|
|
ad6ddcb507 | ||
|
|
6994201d50 | ||
|
|
cc1df6a068 | ||
|
|
88b1552229 | ||
|
|
41b143cb7e | ||
|
|
196ff7f4c1 | ||
|
|
ff16897dde | ||
|
|
300e55e610 | ||
|
|
f716041073 | ||
|
|
a63abe5154 | ||
|
|
c04991f707 | ||
|
|
ddd57517df | ||
|
|
d014b47958 | ||
|
|
6afe18503d | ||
|
|
837dac50fa | ||
|
|
6a4aa184f3 | ||
|
|
20c9280ada | ||
|
|
fea122ae12 | ||
|
|
86c0205faa | ||
|
|
9f76be9d26 | ||
|
|
7705696d54 | ||
|
|
bb7ab625c1 | ||
|
|
da386f2c2e | ||
|
|
0ced28f991 | ||
|
|
9dc39cb758 | ||
|
|
9eacb11f3c | ||
|
|
4b1d15f968 | ||
|
|
7395cc21b8 | ||
|
|
efdba3a482 | ||
|
|
39428841e4 | ||
|
|
981063596a | ||
|
|
67d4eb688a | ||
|
|
da71938d18 | ||
|
|
2850d456d0 | ||
|
|
5063a22155 | ||
|
|
98896ab5ac | ||
|
|
5913e6d187 | ||
|
|
f477aea2a5 | ||
|
|
5803edb77b | ||
|
|
f6ba59ed14 | ||
|
|
542ba89f25 | ||
|
|
2387b66e86 | ||
|
|
75d8d01b17 | ||
|
|
b337d54623 | ||
|
|
44e94bfbf5 | ||
|
|
d93c1ccbaa | ||
|
|
6ca105a290 | ||
|
|
bccd801874 | ||
|
|
eb8800f2d4 | ||
|
|
ac99318f34 | ||
|
|
bc7c88c511 | ||
|
|
f1f955b7ac | ||
|
|
68bc8d0231 | ||
|
|
04a1cff24a | ||
|
|
de15da8a93 | ||
|
|
59938791ef | ||
|
|
3ef0bcc5d5 | ||
|
|
15d2ce150d | ||
|
|
33ae4b176a | ||
|
|
4aae4f9399 | ||
|
|
fc4c31b673 | ||
|
|
88d7f172ca | ||
|
|
bef87c6dff | ||
|
|
747ebd2136 | ||
|
|
41873412e7 | ||
|
|
0811190527 | ||
|
|
0a5ba3490d | ||
|
|
55616a4d1b | ||
|
|
c872cd76e1 | ||
|
|
cf977cb41a | ||
|
|
903aa46e5c | ||
|
|
131efa11be | ||
|
|
5a159d0204 | ||
|
|
7fdeab829f | ||
|
|
4e1b94d37d | ||
|
|
388d743d29 | ||
|
|
8dfccf55d1 | ||
|
|
fafcd02e7c | ||
|
|
defa0ae4d0 | ||
|
|
7db80d20f1 | ||
|
|
a86788f4d7 | ||
|
|
aecc119bac | ||
|
|
8dd3f24285 | ||
|
|
830c6a4894 | ||
|
|
3d37ac9235 | ||
|
|
3deea14559 | ||
|
|
534b578598 | ||
|
|
8355722f4e | ||
|
|
866ff628b7 | ||
|
|
fe9630bb20 | ||
|
|
bca9b3ca3f | ||
|
|
3a25313e61 | ||
|
|
fc66550a32 | ||
|
|
0255d0c59e | ||
|
|
5c12b0e5fa | ||
|
|
aa160e775c | ||
|
|
d3c152022c | ||
|
|
dbb011fc56 | ||
|
|
a6df928d45 | ||
|
|
dd3ae22e08 | ||
|
|
8ff0120642 | ||
|
|
3998fad7ef | ||
|
|
7f8cdf85d5 | ||
|
|
1871425b2d | ||
|
|
5f0ba48309 | ||
|
|
9743dc1ffb | ||
|
|
fcf2b9d1a7 | ||
|
|
9f37820901 | ||
|
|
4295a823c6 | ||
|
|
8191ebfc49 | ||
|
|
3fe12f1249 | ||
|
|
230c83d218 | ||
|
|
11a3308cf5 | ||
|
|
3992ff6b59 | ||
|
|
a05c7a815a | ||
|
|
a7807420fa | ||
|
|
47a52b0587 | ||
|
|
d67a8a4708 | ||
|
|
9c01bbca95 | ||
|
|
554eb3a342 | ||
|
|
9a56b2d20f | ||
|
|
4771ea7cd4 | ||
|
|
aec496d520 | ||
|
|
cb5c59c86c | ||
|
|
628c8e10f7 | ||
|
|
a6361d6221 | ||
|
|
39c5898fa4 | ||
|
|
09d1e3629a | ||
|
|
86ad15612a | ||
|
|
c9152b0b3a | ||
|
|
eb2719fad1 | ||
|
|
a27edcad1c | ||
|
|
15cc4502b4 | ||
|
|
f6ed3dff7f | ||
|
|
101d4f6444 | ||
|
|
fde3ff1bbf | ||
|
|
53de44f272 | ||
|
|
f5c5c32d1d | ||
|
|
9a988d89e3 | ||
|
|
b51f865c54 | ||
|
|
fb46c33d7f | ||
|
|
856ca22aad | ||
|
|
508fa14385 | ||
|
|
5b190c5098 | ||
|
|
6b5e06de50 | ||
|
|
be5f4c9a71 | ||
|
|
7b69282c7e | ||
|
|
54cc3e6315 | ||
|
|
66146c382d | ||
|
|
989f0cc683 | ||
|
|
e02d209e6f | ||
|
|
6445c0563e | ||
|
|
583b0fa778 | ||
|
|
fee517384c | ||
|
|
aa260d263b | ||
|
|
a08dd1f6e1 | ||
|
|
f107866b42 | ||
|
|
7f3c97fb01 | ||
|
|
65cc4d3fbc | ||
|
|
fdd89d65ca | ||
|
|
ecbc0ae57e | ||
|
|
7905694b31 | ||
|
|
335704e176 | ||
|
|
c0bb8a8af7 | ||
|
|
76b8078bd9 | ||
|
|
c6c75a1980 | ||
|
|
93c15e5ee6 | ||
|
|
525cde3498 | ||
|
|
c4d33f9986 | ||
|
|
f0ad78d808 | ||
|
|
83850d9b86 | ||
|
|
0c4bda71fd | ||
|
|
d1687ab963 | ||
|
|
ea0f6b9a12 | ||
|
|
5cc7cb1d85 | ||
|
|
21b1ba1f88 | ||
|
|
1cd126d728 | ||
|
|
6861059d18 | ||
|
|
80d9938e96 | ||
|
|
fddcdf359b | ||
|
|
cdda7f8f9a | ||
|
|
086e46c162 | ||
|
|
ed061252a5 | ||
|
|
7c4e4d7fa2 | ||
|
|
4180ed09a5 | ||
|
|
f50bf0b97f | ||
|
|
2c75b4836d | ||
|
|
292e57ffc7 | ||
|
|
1a0e524b49 | ||
|
|
ffc20e4492 | ||
|
|
f2a5862714 | ||
|
|
faeb1483f2 | ||
|
|
a4c2138e74 | ||
|
|
1c5d410373 | ||
|
|
b9fb9af74f | ||
|
|
812dcb5e8d | ||
|
|
703b944839 | ||
|
|
e1ba9f8ff8 | ||
|
|
3db696d52f | ||
|
|
6d0dbebda9 | ||
|
|
f4d9618487 | ||
|
|
b7077eb71d | ||
|
|
1a4d326abb | ||
|
|
48cbdd9d40 | ||
|
|
26532ab9b4 | ||
|
|
c0e780a28f | ||
|
|
5c4daeee4c | ||
|
|
1c5abaa518 | ||
|
|
5586d231de | ||
|
|
3fbb643d51 | ||
|
|
766e7dadb1 | ||
|
|
ee9763c98f | ||
|
|
5e7aa4ff81 | ||
|
|
f162462111 | ||
|
|
088d23d557 | ||
|
|
fbcd5e2f1e | ||
|
|
c302219f9e | ||
|
|
21d136e224 | ||
|
|
487ddb5694 | ||
|
|
873ccf8096 | ||
|
|
696478843e | ||
|
|
2690618da2 | ||
|
|
68a0e32a3d | ||
|
|
449986456e | ||
|
|
2363a6bd44 | ||
|
|
41ed2d1b84 | ||
|
|
b873fee1cf | ||
|
|
e70052e966 | ||
|
|
101fdb1fba | ||
|
|
ebe4bbbf0f | ||
|
|
63669c1612 | ||
|
|
1c8db1e2e9 | ||
|
|
62463d2c3e | ||
|
|
247f8f4fcc | ||
|
|
34d15e7216 | ||
|
|
d46a5f693f | ||
|
|
8bfe407610 | ||
|
|
08e3a54a58 | ||
|
|
8c92f42de3 | ||
|
|
07106897a6 | ||
|
|
5d5b89c82d | ||
|
|
e42cc02d0e | ||
|
|
11a46a1072 | ||
|
|
a0fbbf2fb6 | ||
|
|
74e46f7b80 | ||
|
|
b8d1b8d6c1 | ||
|
|
7ae1ef0a1a | ||
|
|
13aa42c883 |
@@ -1,30 +0,0 @@
|
||||
@echo off
|
||||
|
||||
call:checkCommitMessage
|
||||
GOTO:EOF
|
||||
|
||||
:checkCommitMessage
|
||||
call:logInfo "Commit message: %APPVEYOR_REPO_COMMIT_MESSAGE%"
|
||||
call:logInfo "Is pull request: %APPVEYOR_PULL_REQUEST_NUMBER%"
|
||||
|
||||
if not "%APPVEYOR_PULL_REQUEST_NUMBER%" == "" (
|
||||
ECHO "%APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED%" | FINDSTR /C:"Signed-off-by: " >nul & IF ERRORLEVEL 1 (
|
||||
call:logError "The commit message does not contain the signature!"
|
||||
call:logError "More information: https://github.com/telegramdesktop/tdesktop/blob/master/.github/CONTRIBUTING.md#sign-your-work"
|
||||
exit 1
|
||||
) else (
|
||||
call:logInfo "Commit message contains signature"
|
||||
|
||||
:: Reset error level
|
||||
verify >nul
|
||||
)
|
||||
)
|
||||
GOTO:EOF
|
||||
|
||||
:logInfo
|
||||
echo [INFO] %~1
|
||||
GOTO:EOF
|
||||
|
||||
:logError
|
||||
echo [ERROR] %~1
|
||||
GOTO:EOF
|
||||
@@ -1,12 +1,10 @@
|
||||
@echo off
|
||||
|
||||
SET BUILD_DIR=C:\TBuild
|
||||
IF "%BUILD_DIR%"=="" SET BUILD_DIR=C:\TBuild
|
||||
SET LIB_DIR=%BUILD_DIR%\Libraries
|
||||
SET SRC_DIR=%BUILD_DIR%\tdesktop
|
||||
SET QT_VERSION=5_6_2
|
||||
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86
|
||||
|
||||
call:configureBuild
|
||||
call:getDependencies
|
||||
call:setupGYP
|
||||
@@ -29,7 +27,7 @@ GOTO:EOF
|
||||
call:logInfo "Clone dependencies repository"
|
||||
git clone -q --depth 1 --branch=master https://github.com/telegramdesktop/dependencies_windows.git %LIB_DIR%
|
||||
cd %LIB_DIR%
|
||||
|
||||
git clone https://github.com/Microsoft/Range-V3-VS2015 range-v3
|
||||
if exist prepare.bat (
|
||||
call prepare.bat
|
||||
) else (
|
||||
@@ -45,7 +43,7 @@ GOTO:EOF
|
||||
git clone https://chromium.googlesource.com/external/gyp
|
||||
cd gyp
|
||||
git checkout a478c1ab51
|
||||
SET PATH=%PATH%;C:\TBuild\Libraries\gyp;C:\TBuild\Libraries\ninja;
|
||||
SET PATH=%PATH%;%BUILD_DIR%\Libraries\gyp;%BUILD_DIR%\Libraries\ninja;
|
||||
cd %SRC_DIR%
|
||||
git submodule init
|
||||
git submodule update
|
||||
@@ -82,6 +80,10 @@ GOTO:EOF
|
||||
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_UNITY_INTEGRATION
|
||||
)
|
||||
|
||||
echo %BUILD_VERSION% | findstr /C:"disable_gtk_integration">nul && (
|
||||
set TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES%,TDESKTOP_DISABLE_GTK_INTEGRATION
|
||||
)
|
||||
|
||||
if not "%TDESKTOP_BUILD_DEFINES%" == "" (
|
||||
set "TDESKTOP_BUILD_DEFINES=%TDESKTOP_BUILD_DEFINES:~1%"
|
||||
)
|
||||
|
||||
49
.github/CONTRIBUTING.md
vendored
@@ -5,8 +5,6 @@ This document describes how you can contribute to Telegram Desktop. Please read
|
||||
**Table of Contents**
|
||||
|
||||
* [What contributions are accepted](#what-contributions-are-accepted)
|
||||
* [Sign your work](#sign-your-work)
|
||||
* [Change commit message of a pushed commit](#change-commit-message-of-a-pushed-commit)
|
||||
* [Build instructions](#build-instructions)
|
||||
* [Pull upstream changes into your fork regularly](#pull-upstream-changes-into-your-fork-regularly)
|
||||
* [How to get your pull request accepted](#how-to-get-your-pull-request-accepted)
|
||||
@@ -28,49 +26,6 @@ Unfortunately we **do not merge** any pull requests that have new feature implem
|
||||
|
||||
Telegram Desktop is not a standalone application but a part of [Telegram project][telegram], so all the decisions about the features, languages, user experience, user interface and the design are made inside Telegram team, often according to some roadmap which is not public.
|
||||
|
||||
## Sign your work
|
||||
|
||||
For contributions to be accepted they should be granted into the public domain. This will solve the issue if Telegram team needs to use full Telegram Desktop source code with some different license.
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for the patch. Your signature certifies that you wrote the patch and you have the right to put it in the public domain. The rules are pretty simple: if you can certify the below:
|
||||
|
||||
```
|
||||
Telegram Desktop Developer Certificate of Origin
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole by me or is based upon
|
||||
previous work that, to the best of my knowledge, is in the
|
||||
public domain and I have the right to put it in the public domain.
|
||||
|
||||
(d) I understand and agree that this project and the contribution are
|
||||
public and that a record of the contribution (including all
|
||||
metadata and personal information I submit with it, including my
|
||||
sign-off) is maintained indefinitely and may be redistributed.
|
||||
|
||||
(e) I am granting this work into the public domain.
|
||||
```
|
||||
|
||||
Then you just add a line to every **git commit message** that states:
|
||||
|
||||
Signed-off-by: Random J Developer <random@developer.example.org> (github: rndjdev_github)
|
||||
|
||||
Replacing Random Developer’s details with your name, email address and GitHub username.
|
||||
|
||||
### Change commit message of a pushed commit
|
||||
|
||||
If you already pushed a commit and forgot to add the signature to the commit message, follow these steps to change the message of the commit:
|
||||
|
||||
1. Open `Git Bash` (or `Git Shell`)
|
||||
2. Enter following command to change the commit message of the most recent commit: `git commit --amend`
|
||||
3. Press <kbd>i</kbd> to get into Insert-mode
|
||||
4. Change the commit message (and add the [signature](#sign-your-work) at the and)
|
||||
5. After editing the message, press <kbd>ESC</kbd> to get out of the Insert-mode
|
||||
6. Write `:wq` and press <kbd>Enter</kbd> to save the new message or write `:q!` to discard your changes
|
||||
7. Enter `git push --force` to push the commit with the new commit message to the remote repository
|
||||
|
||||
For more info, see [GitHub Help][help_change_commit_message].
|
||||
|
||||
## Build instructions
|
||||
|
||||
See the [README.md][build_instructions] for details on the various build
|
||||
@@ -118,7 +73,7 @@ If you already have multiple commits, you can add the commits together (squash t
|
||||
1. Open `Git Bash` (or `Git Shell`)
|
||||
2. Enter following command to squash the recent {N} commits: `git reset --soft HEAD~{N} && git commit` (replace `{N}` with the number of commits you want to squash)
|
||||
3. Press <kbd>i</kbd> to get into Insert-mode
|
||||
4. Enter the commit message of the new commit (and add the [signature](#sign-your-work) at the end)
|
||||
4. Enter the commit message of the new commit
|
||||
5. After adding the message, press <kbd>ESC</kbd> to get out of the Insert-mode
|
||||
6. Write `:wq` and press <kbd>Enter</kbd> to save the new message or write `:q!` to discard your changes
|
||||
7. Enter `git push --force` to push the new commit to the remote repository
|
||||
@@ -150,8 +105,6 @@ Before you submit a pull request, please test your changes. Verify that Telegram
|
||||
|
||||
For example: `Fix #545`
|
||||
|
||||
* Don't forget to [sign your patch](#sign-your-work) to put it in the public domain!
|
||||
|
||||
[//]: # (LINKS)
|
||||
[telegram]: https://telegram.org/
|
||||
[help_fork_repo]: https://help.github.com/articles/fork-a-repo/
|
||||
|
||||
1
.gitignore
vendored
@@ -34,6 +34,7 @@
|
||||
*.xcodeproj
|
||||
/Win32/
|
||||
ipch/
|
||||
.vs/
|
||||
|
||||
/Telegram/log.txt
|
||||
/Telegram/data
|
||||
|
||||
11
.travis.yml
@@ -16,6 +16,7 @@ env:
|
||||
- BUILD_VERSION="disable_network_proxy"
|
||||
- BUILD_VERSION="disable_desktop_file_generation"
|
||||
- BUILD_VERSION="disable_unity_integration"
|
||||
- BUILD_VERSION="disable_gtk_integration"
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
@@ -32,8 +33,8 @@ addons:
|
||||
- dpatch
|
||||
- equivs
|
||||
- fakeroot
|
||||
- g++-6
|
||||
- gcc-6
|
||||
- g++-7
|
||||
- gcc-7
|
||||
- git
|
||||
- gnome-common
|
||||
- gobject-introspection
|
||||
@@ -57,10 +58,8 @@ addons:
|
||||
- yasm
|
||||
|
||||
before_install:
|
||||
- "export TRAVIS_COMMIT_MSG=\"$(git log --format=%B --no-merges -n 1)\""
|
||||
- .travis/check.sh
|
||||
- export CXX="g++-6" CC="gcc-6"
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-6 60 --slave /usr/bin/g++ g++ /usr/bin/g++-6
|
||||
- export CXX="g++-7" CC="gcc-7"
|
||||
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 --slave /usr/bin/g++ g++ /usr/bin/g++-7
|
||||
- sudo update-alternatives --config gcc
|
||||
- g++ --version
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ GYP_PATH="$BUILD/gyp"
|
||||
GYP_CACHE_VERSION="3"
|
||||
GYP_PATCH="$UPSTREAM/Telegram/Patches/gyp.diff"
|
||||
|
||||
RANGE_PATH="$BUILD/range-v3"
|
||||
RANGE_CACHE_VERSION="3"
|
||||
|
||||
VA_PATH="$BUILD/libva"
|
||||
VA_CACHE_VERSION="3"
|
||||
|
||||
@@ -85,6 +88,9 @@ build() {
|
||||
# Patched GYP (supports cmake precompiled headers)
|
||||
getGYP
|
||||
|
||||
# Range v3
|
||||
getRange
|
||||
|
||||
# Guideline Support Library
|
||||
getGSL
|
||||
|
||||
@@ -118,6 +124,10 @@ build() {
|
||||
GYP_DEFINES+=",TDESKTOP_DISABLE_UNITY_INTEGRATION"
|
||||
fi
|
||||
|
||||
if [[ $BUILD_VERSION == *"disable_gtk_integration"* ]]; then
|
||||
GYP_DEFINES+=",TDESKTOP_DISABLE_GTK_INTEGRATION"
|
||||
fi
|
||||
|
||||
info_msg "Build defines: ${GYP_DEFINES}"
|
||||
|
||||
buildTelegram
|
||||
@@ -174,6 +184,52 @@ buildXkbCommon() {
|
||||
sudo ldconfig
|
||||
}
|
||||
|
||||
getRange() {
|
||||
travisStartFold "Getting range-v3"
|
||||
|
||||
local RANGE_CACHE="$CACHE/range-v3"
|
||||
local RANGE_CACHE_FILE="$RANGE_CACHE/.cache.txt"
|
||||
local RANGE_CACHE_KEY="${RANGE_CACHE_VERSION}"
|
||||
local RANGE_CACHE_OUTDATED="1"
|
||||
|
||||
if [ ! -d "$RANGE_CACHE" ]; then
|
||||
mkdir -p "$RANGE_CACHE"
|
||||
fi
|
||||
|
||||
ln -sf "$RANGE_CACHE" "$RANGE_PATH"
|
||||
|
||||
if [ -f "$RANGE_CACHE_FILE" ]; then
|
||||
local RANGE_CACHE_KEY_FOUND=`tail -n 1 $RANGE_CACHE_FILE`
|
||||
if [ "$RANGE_CACHE_KEY" == "$RANGE_CACHE_KEY_FOUND" ]; then
|
||||
RANGE_CACHE_OUTDATED="0"
|
||||
else
|
||||
info_msg "Cache key '$RANGE_CACHE_KEY_FOUND' does not match '$RANGE_CACHE_KEY', getting range-v3"
|
||||
fi
|
||||
fi
|
||||
if [ "$RANGE_CACHE_OUTDATED" == "1" ]; then
|
||||
buildRange
|
||||
sudo echo $RANGE_CACHE_KEY > "$RANGE_CACHE_FILE"
|
||||
else
|
||||
info_msg "Using cached range-v3"
|
||||
fi
|
||||
}
|
||||
|
||||
buildRange() {
|
||||
info_msg "Downloading range-v3"
|
||||
|
||||
if [ -d "$EXTERNAL/range-v3" ]; then
|
||||
rm -rf "$EXTERNAL/range-v3"
|
||||
fi
|
||||
cd $RANGE_PATH
|
||||
rm -rf *
|
||||
|
||||
cd "$EXTERNAL"
|
||||
git clone --depth=1 https://github.com/ericniebler/range-v3
|
||||
|
||||
cd "$EXTERNAL/range-v3"
|
||||
cp -r * "$RANGE_PATH/"
|
||||
}
|
||||
|
||||
getVa() {
|
||||
travisStartFold "Getting libva"
|
||||
|
||||
@@ -315,6 +371,8 @@ buildFFmpeg() {
|
||||
git clone https://git.ffmpeg.org/ffmpeg.git
|
||||
|
||||
cd "$EXTERNAL/ffmpeg"
|
||||
git checkout release/3.4
|
||||
|
||||
./configure \
|
||||
--prefix=$FFMPEG_PATH \
|
||||
--disable-debug \
|
||||
@@ -542,6 +600,11 @@ buildCustomQt() {
|
||||
git apply "$QT_PATCH"
|
||||
cd ..
|
||||
|
||||
cd "$EXTERNAL/qt${QT_VERSION}/qtbase/src/plugins/platforminputcontexts"
|
||||
git clone https://github.com/telegramdesktop/fcitx.git
|
||||
git clone https://github.com/telegramdesktop/hime.git
|
||||
cd ../../../..
|
||||
|
||||
./configure -prefix $QT_PATH -release -opensource -confirm-license -qt-zlib \
|
||||
-qt-libpng -qt-libjpeg -qt-freetype -qt-harfbuzz -qt-pcre -qt-xcb \
|
||||
-qt-xkbcommon-x11 -no-opengl -no-gtkstyle -static \
|
||||
@@ -617,6 +680,7 @@ buildTelegram() {
|
||||
-Dlinux_path_vdpau=$VDPAU_PATH \
|
||||
-Dlinux_path_ffmpeg=$FFMPEG_PATH \
|
||||
-Dlinux_path_openal=$OPENAL_PATH \
|
||||
-Dlinux_path_range=$RANGE_PATH \
|
||||
-Dlinux_path_qt=$QT_PATH \
|
||||
-Dlinux_path_breakpad=$BREAKPAD_PATH \
|
||||
-Dlinux_path_libexif_lib=/usr/local/lib \
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Checks if the commit message contains the signature
|
||||
|
||||
run() {
|
||||
checkCommitMessage
|
||||
}
|
||||
|
||||
checkCommitMessage() {
|
||||
info_msg "Commit message: ${TRAVIS_COMMIT_MSG}";
|
||||
info_msg "Is pull request: ${TRAVIS_PULL_REQUEST}";
|
||||
|
||||
if [[ $TRAVIS_PULL_REQUEST != "false" ]];then
|
||||
if [[ $TRAVIS_COMMIT_MSG != *"Signed-off-by: "* ]];then
|
||||
error_msg "The commit message does not contain the signature!"
|
||||
error_msg "More information: https://github.com/telegramdesktop/tdesktop/blob/master/.github/CONTRIBUTING.md#sign-your-work"
|
||||
exit 1
|
||||
else
|
||||
success_msg "Commit message contains signature"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
source ./.travis/common.sh
|
||||
|
||||
run
|
||||
@@ -35,14 +35,15 @@ The source code is published under GPLv3 with OpenSSL exception, the license is
|
||||
* FFmpeg ([LGPL](https://www.ffmpeg.org/legal.html))
|
||||
* Guideline Support Library ([MIT License](https://github.com/Microsoft/GSL/blob/master/LICENSE))
|
||||
* Mapbox Variant ([BSD License](https://github.com/mapbox/variant/blob/master/LICENSE))
|
||||
* Range-v3 ([Boost License](https://github.com/ericniebler/range-v3/blob/master/LICENSE.txt))
|
||||
* Open Sans font ([Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0.html))
|
||||
* Emoji alpha codes ([MIT License](https://github.com/emojione/emojione/blob/master/extras/alpha-codes/LICENSE.md))
|
||||
* Catch test framework ([Boost License](https://github.com/philsquared/Catch/blob/master/LICENSE.txt))
|
||||
|
||||
## Build instructions
|
||||
|
||||
* [Visual Studio 2015][msvc]
|
||||
* [Xcode 8][xcode]
|
||||
* [Visual Studio 2017][msvc]
|
||||
* [Xcode 9][xcode]
|
||||
* [GYP/CMake on GNU/Linux][cmake]
|
||||
|
||||
[//]: # (LINKS)
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
diff --git a/src/build/common.gypi b/src/build/common.gypi
|
||||
index 29990c6..53e99d4 100644
|
||||
--- a/src/build/common.gypi
|
||||
+++ b/src/build/common.gypi
|
||||
@@ -330,6 +330,7 @@
|
||||
'VCCLCompilerTool': {
|
||||
'WarnAsError': 'true',
|
||||
'Detect64BitPortabilityProblems': 'false',
|
||||
+ 'TreatWChar_tAsBuiltInType': 'false',
|
||||
},
|
||||
},
|
||||
}],
|
||||
diff --git a/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
|
||||
index 584ec5d..1c7214f 100644
|
||||
index 1a93ce6..1c1d643 100644
|
||||
--- a/src/client/mac/Breakpad.xcodeproj/project.pbxproj
|
||||
+++ b/src/client/mac/Breakpad.xcodeproj/project.pbxproj
|
||||
@@ -35,6 +35,19 @@
|
||||
@@ -77,7 +89,7 @@ index 584ec5d..1c7214f 100644
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@@ -715,7 +703,6 @@
|
||||
@@ -714,7 +702,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -85,7 +97,7 @@ index 584ec5d..1c7214f 100644
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -1183,18 +1170,13 @@
|
||||
@@ -1181,18 +1168,13 @@
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */;
|
||||
buildPhases = (
|
||||
@@ -104,7 +116,7 @@ index 584ec5d..1c7214f 100644
|
||||
);
|
||||
name = Breakpad;
|
||||
productInstallPath = "$(HOME)/Library/Frameworks";
|
||||
@@ -1401,6 +1383,8 @@
|
||||
@@ -1399,6 +1381,8 @@
|
||||
/* Begin PBXProject section */
|
||||
0867D690FE84028FC02AAC07 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
@@ -113,7 +125,7 @@ index 584ec5d..1c7214f 100644
|
||||
buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */;
|
||||
compatibilityVersion = "Xcode 3.1";
|
||||
developmentRegion = English;
|
||||
@@ -1585,16 +1569,6 @@
|
||||
@@ -1583,16 +1567,6 @@
|
||||
/* End PBXReferenceProxy section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
@@ -130,7 +142,7 @@ index 584ec5d..1c7214f 100644
|
||||
F92C569C0ECE04A7009BE4BA /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1642,20 +1616,6 @@
|
||||
@@ -1640,20 +1614,6 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n";
|
||||
};
|
||||
@@ -151,7 +163,7 @@ index 584ec5d..1c7214f 100644
|
||||
F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -1676,6 +1636,19 @@
|
||||
@@ -1674,6 +1634,19 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
@@ -171,7 +183,7 @@ index 584ec5d..1c7214f 100644
|
||||
F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */,
|
||||
F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */,
|
||||
F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */,
|
||||
@@ -1957,16 +1930,6 @@
|
||||
@@ -1955,16 +1928,6 @@
|
||||
target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */;
|
||||
targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */;
|
||||
};
|
||||
@@ -188,7 +200,7 @@ index 584ec5d..1c7214f 100644
|
||||
F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = F93803BD0F80820F004D428B /* generator_test */;
|
||||
@@ -2027,11 +1990,6 @@
|
||||
@@ -2025,11 +1988,6 @@
|
||||
target = 8DC2EF4F0486A6940098B216 /* Breakpad */;
|
||||
targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */;
|
||||
};
|
||||
@@ -200,7 +212,7 @@ index 584ec5d..1c7214f 100644
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
@@ -2128,8 +2086,12 @@
|
||||
@@ -2126,8 +2084,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
|
||||
buildSettings = {
|
||||
@@ -214,7 +226,7 @@ index 584ec5d..1c7214f 100644
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
@@ -2137,7 +2099,12 @@
|
||||
@@ -2135,7 +2097,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */;
|
||||
buildSettings = {
|
||||
@@ -227,7 +239,7 @@ index 584ec5d..1c7214f 100644
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
@@ -2456,7 +2423,12 @@
|
||||
@@ -2454,7 +2421,12 @@
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */;
|
||||
buildSettings = {
|
||||
@@ -467,3 +479,151 @@ index 1d2e519..943310f 100644
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/src/common/language.cc b/src/common/language.cc
|
||||
index 978fb85..a95ae5f 100644
|
||||
--- a/src/common/language.cc
|
||||
+++ b/src/common/language.cc
|
||||
@@ -46,8 +46,27 @@
|
||||
|
||||
#include <limits>
|
||||
|
||||
+#include <cstdio>
|
||||
+#include <iostream>
|
||||
+#include <memory>
|
||||
+#include <stdexcept>
|
||||
+#include <string>
|
||||
+#include <array>
|
||||
+
|
||||
namespace {
|
||||
|
||||
+std::string exec(std::string cmd) {
|
||||
+ std::array<char, 128> buffer;
|
||||
+ std::string result;
|
||||
+ std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
|
||||
+ if (!pipe) throw std::runtime_error("popen() failed!");
|
||||
+ while (!feof(pipe.get())) {
|
||||
+ if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
|
||||
+ result += buffer.data();
|
||||
+ }
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
string MakeQualifiedNameWithSeparator(const string& parent_name,
|
||||
const char* separator,
|
||||
const string& name) {
|
||||
@@ -79,11 +98,29 @@ class CPPLanguage: public Language {
|
||||
demangled->clear();
|
||||
return kDontDemangle;
|
||||
#else
|
||||
+ DemangleResult result;
|
||||
+ if (mangled.find("type_erased_handlers") != std::string::npos
|
||||
+ && mangled.find("vtable_once_impl") != std::string::npos) {
|
||||
+
|
||||
+ auto demangled_str = exec("c++filt " + mangled);
|
||||
+ if (!demangled_str.empty() && demangled_str.back() == '\n') {
|
||||
+ demangled_str.pop_back();
|
||||
+ }
|
||||
+ if (demangled_str != mangled) {
|
||||
+ result = kDemangleSuccess;
|
||||
+ demangled->assign(demangled_str.c_str());
|
||||
+ } else {
|
||||
+ result = kDemangleFailure;
|
||||
+ demangled->clear();
|
||||
+ }
|
||||
+
|
||||
+ } else {
|
||||
+
|
||||
int status;
|
||||
char* demangled_c =
|
||||
abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
|
||||
- DemangleResult result;
|
||||
+// DemangleResult result;
|
||||
if (status == 0) {
|
||||
result = kDemangleSuccess;
|
||||
demangled->assign(demangled_c);
|
||||
@@ -96,6 +133,8 @@ class CPPLanguage: public Language {
|
||||
free(reinterpret_cast<void*>(demangled_c));
|
||||
}
|
||||
|
||||
+ }
|
||||
+
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
diff --git a/src/common/linux/elf_symbols_to_module.cc b/src/common/linux/elf_symbols_to_module.cc
|
||||
index 562875e..4367851 100644
|
||||
--- a/src/common/linux/elf_symbols_to_module.cc
|
||||
+++ b/src/common/linux/elf_symbols_to_module.cc
|
||||
@@ -39,6 +39,29 @@
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/module.h"
|
||||
|
||||
+#include <cstdio>
|
||||
+#include <iostream>
|
||||
+#include <memory>
|
||||
+#include <stdexcept>
|
||||
+#include <string>
|
||||
+#include <array>
|
||||
+
|
||||
+namespace {
|
||||
+
|
||||
+std::string exec(std::string cmd) {
|
||||
+ std::array<char, 128> buffer;
|
||||
+ std::string result;
|
||||
+ std::shared_ptr<FILE> pipe(popen(cmd.c_str(), "r"), pclose);
|
||||
+ if (!pipe) throw std::runtime_error("popen() failed!");
|
||||
+ while (!feof(pipe.get())) {
|
||||
+ if (fgets(buffer.data(), 128, pipe.get()) != nullptr)
|
||||
+ result += buffer.data();
|
||||
+ }
|
||||
+ return result;
|
||||
+}
|
||||
+
|
||||
+}
|
||||
+
|
||||
namespace google_breakpad {
|
||||
|
||||
class ELFSymbolIterator {
|
||||
@@ -159,6 +182,19 @@ bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
Module::Extern *ext = new Module::Extern(iterator->value);
|
||||
ext->name = SymbolString(iterator->name_offset, strings);
|
||||
#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
|
||||
+ if (ext->name.find("type_erased_handlers") != std::string::npos
|
||||
+ && ext->name.find("vtable_once_impl") != std::string::npos) {
|
||||
+
|
||||
+ auto demangled_str = exec("c++filt " + ext->name);
|
||||
+ if (!demangled_str.empty() && demangled_str.back() == '\n') {
|
||||
+ demangled_str.pop_back();
|
||||
+ }
|
||||
+ if (demangled_str != ext->name) {
|
||||
+ ext->name = demangled_str;
|
||||
+ }
|
||||
+
|
||||
+ } else {
|
||||
+
|
||||
int status = 0;
|
||||
char* demangled =
|
||||
abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status);
|
||||
@@ -167,6 +203,8 @@ bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
ext->name = demangled;
|
||||
free(demangled);
|
||||
}
|
||||
+
|
||||
+ }
|
||||
#endif
|
||||
module->AddExtern(ext);
|
||||
}
|
||||
diff --git a/src/tools/linux/tools_linux.gypi b/src/tools/linux/tools_linux.gypi
|
||||
index 1c15992..020e4c1 100644
|
||||
--- a/src/tools/linux/tools_linux.gypi
|
||||
+++ b/src/tools/linux/tools_linux.gypi
|
||||
@@ -58,7 +58,7 @@
|
||||
'target_name': 'minidump_upload',
|
||||
'type': 'executable',
|
||||
'sources': [
|
||||
- 'symupload/minidump_upload.m',
|
||||
+ 'symupload/minidump_upload.cc',
|
||||
],
|
||||
'dependencies': [
|
||||
'../common/common.gyp:common',
|
||||
18
Telegram/Patches/build_ffmpeg_win.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
set -e
|
||||
FullExecPath=$PWD
|
||||
pushd `dirname $0` > /dev/null
|
||||
FullScriptPath=`pwd`
|
||||
popd > /dev/null
|
||||
|
||||
pacman --noconfirm -Sy
|
||||
pacman --noconfirm -S msys/make
|
||||
pacman --noconfirm -S mingw64/mingw-w64-x86_64-opus
|
||||
pacman --noconfirm -S diffutils
|
||||
pacman --noconfirm -S pkg-config
|
||||
|
||||
PKG_CONFIG_PATH="/mingw64/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
|
||||
./configure --toolchain=msvc --disable-programs --disable-doc --disable-everything --enable-protocol=file --enable-libopus --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=aasc --enable-decoder=flac --enable-decoder=gif --enable-decoder=h264 --enable-decoder=mp1 --enable-decoder=mp1float --enable-decoder=mp2 --enable-decoder=mp2float --enable-decoder=mp3 --enable-decoder=mp3adu --enable-decoder=mp3adufloat --enable-decoder=mp3float --enable-decoder=mp3on4 --enable-decoder=mp3on4float --enable-decoder=mpeg4 --enable-decoder=msmpeg4v2 --enable-decoder=msmpeg4v3 --enable-decoder=wavpack --enable-decoder=opus --enable-decoder=pcm_alaw --enable-decoder=pcm_alaw_at --enable-decoder=pcm_f32be --enable-decoder=pcm_f32le --enable-decoder=pcm_f64be --enable-decoder=pcm_f64le --enable-decoder=pcm_lxf --enable-decoder=pcm_mulaw --enable-decoder=pcm_mulaw_at --enable-decoder=pcm_s16be --enable-decoder=pcm_s16be_planar --enable-decoder=pcm_s16le --enable-decoder=pcm_s16le_planar --enable-decoder=pcm_s24be --enable-decoder=pcm_s24daud --enable-decoder=pcm_s24le --enable-decoder=pcm_s24le_planar --enable-decoder=pcm_s32be --enable-decoder=pcm_s32le --enable-decoder=pcm_s32le_planar --enable-decoder=pcm_s64be --enable-decoder=pcm_s64le --enable-decoder=pcm_s8 --enable-decoder=pcm_s8_planar --enable-decoder=pcm_u16be --enable-decoder=pcm_u16le --enable-decoder=pcm_u24be --enable-decoder=pcm_u24le --enable-decoder=pcm_u32be --enable-decoder=pcm_u32le --enable-decoder=pcm_u8 --enable-decoder=pcm_zork --enable-decoder=vorbis --enable-decoder=wmalossless --enable-decoder=wmapro --enable-decoder=wmav1 --enable-decoder=wmav2 --enable-decoder=wmavoice --enable-encoder=libopus --enable-hwaccel=h264_d3d11va --enable-hwaccel=h264_dxva2 --enable-parser=aac --enable-parser=aac_latm --enable-parser=flac --enable-parser=h264 --enable-parser=mpeg4video --enable-parser=mpegaudio --enable-parser=opus --enable-parser=vorbis --enable-demuxer=aac --enable-demuxer=flac --enable-demuxer=gif --enable-demuxer=h264 --enable-demuxer=mov --enable-demuxer=mp3 --enable-demuxer=ogg --enable-demuxer=wav --enable-muxer=ogg --enable-muxer=opus --extra-ldflags="-libpath:$FullExecPath/../opus/win32/VS2015/Win32/Release"
|
||||
|
||||
make -j4
|
||||
make -j4 install
|
||||
@@ -78,6 +78,9 @@ slideDuration: 240;
|
||||
slideShift: 100px;
|
||||
slideShadow: icon {{ "slide_shadow", slideFadeOutShadowFg }};
|
||||
|
||||
slideWrapDuration: 150;
|
||||
fadeWrapDuration: 200;
|
||||
|
||||
linkCropLimit: 360px;
|
||||
linkFont: normalFont;
|
||||
linkOverFont: font(fsize underline);
|
||||
@@ -288,7 +291,7 @@ inlineRowBorder: 1px;
|
||||
inlineRowBorderFg: shadowFg;
|
||||
inlineRowFileNameTop: 2px;
|
||||
inlineRowFileDescriptionTop: 23px;
|
||||
inlineResultsMinWidth: 64px;
|
||||
inlineResultsMinWidth: 48px;
|
||||
inlineDurationMargin: 3px;
|
||||
|
||||
toastTextStyle: defaultTextStyle;
|
||||
|
||||
@@ -316,6 +316,7 @@ historyPeer8NameFg: #ce671b; // orange group member name
|
||||
historyPeer8NameFgSelected: historyPeer8NameFg; // orange group member name in a selected message
|
||||
historyPeer8UserpicBg: #faa774; // orange userpic background
|
||||
historyPeerUserpicFg: windowFgActive; // default userpic initials
|
||||
historyPeerSavedMessagesBg: historyPeer4UserpicBg; // saved messages userpic background
|
||||
|
||||
// Some values are marked as (adjusted), it means they're adjusted by
|
||||
// hue and saturation of the average background color if user chooses
|
||||
|
||||
BIN
Telegram/Resources/icons/info_actions.png
Normal file
|
After Width: | Height: | Size: 136 B |
BIN
Telegram/Resources/icons/info_actions@2x.png
Normal file
|
After Width: | Height: | Size: 242 B |
BIN
Telegram/Resources/icons/info_add_member.png
Normal file
|
After Width: | Height: | Size: 572 B |
BIN
Telegram/Resources/icons/info_add_member@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info_administrators.png
Normal file
|
After Width: | Height: | Size: 807 B |
BIN
Telegram/Resources/icons/info_administrators@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/info_back.png
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
Telegram/Resources/icons/info_back@2x.png
Normal file
|
After Width: | Height: | Size: 556 B |
BIN
Telegram/Resources/icons/info_blacklist.png
Normal file
|
After Width: | Height: | Size: 600 B |
BIN
Telegram/Resources/icons/info_blacklist@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info_close.png
Normal file
|
After Width: | Height: | Size: 275 B |
BIN
Telegram/Resources/icons/info_close@2x.png
Normal file
|
After Width: | Height: | Size: 575 B |
BIN
Telegram/Resources/icons/info_common_groups.png
Normal file
|
After Width: | Height: | Size: 551 B |
BIN
Telegram/Resources/icons/info_common_groups@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info_information.png
Normal file
|
After Width: | Height: | Size: 593 B |
BIN
Telegram/Resources/icons/info_information@2x.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/info_media_audio.png
Normal file
|
After Width: | Height: | Size: 588 B |
BIN
Telegram/Resources/icons/info_media_audio@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/info_media_audio_empty.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
Telegram/Resources/icons/info_media_audio_empty@2x.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
Telegram/Resources/icons/info_media_delete.png
Normal file
|
After Width: | Height: | Size: 189 B |
BIN
Telegram/Resources/icons/info_media_delete@2x.png
Normal file
|
After Width: | Height: | Size: 351 B |
BIN
Telegram/Resources/icons/info_media_file.png
Normal file
|
After Width: | Height: | Size: 225 B |
BIN
Telegram/Resources/icons/info_media_file@2x.png
Normal file
|
After Width: | Height: | Size: 424 B |
BIN
Telegram/Resources/icons/info_media_file_empty.png
Normal file
|
After Width: | Height: | Size: 952 B |
BIN
Telegram/Resources/icons/info_media_file_empty@2x.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Telegram/Resources/icons/info_media_forward.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
Telegram/Resources/icons/info_media_forward@2x.png
Normal file
|
After Width: | Height: | Size: 390 B |
BIN
Telegram/Resources/icons/info_media_link.png
Normal file
|
After Width: | Height: | Size: 398 B |
BIN
Telegram/Resources/icons/info_media_link@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/info_media_link_empty.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info_media_link_empty@2x.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Telegram/Resources/icons/info_media_photo.png
Normal file
|
After Width: | Height: | Size: 337 B |
BIN
Telegram/Resources/icons/info_media_photo@2x.png
Normal file
|
After Width: | Height: | Size: 486 B |
BIN
Telegram/Resources/icons/info_media_photo_empty.png
Normal file
|
After Width: | Height: | Size: 894 B |
BIN
Telegram/Resources/icons/info_media_photo_empty@2x.png
Normal file
|
After Width: | Height: | Size: 2.1 KiB |
BIN
Telegram/Resources/icons/info_media_round.png
Normal file
|
After Width: | Height: | Size: 664 B |
BIN
Telegram/Resources/icons/info_media_round@2x.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/info_media_video.png
Normal file
|
After Width: | Height: | Size: 234 B |
BIN
Telegram/Resources/icons/info_media_video@2x.png
Normal file
|
After Width: | Height: | Size: 458 B |
BIN
Telegram/Resources/icons/info_media_video_empty.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/info_media_video_empty@2x.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Telegram/Resources/icons/info_media_voice.png
Normal file
|
After Width: | Height: | Size: 476 B |
BIN
Telegram/Resources/icons/info_media_voice@2x.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Telegram/Resources/icons/info_media_voice_empty.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Telegram/Resources/icons/info_media_voice_empty@2x.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
Telegram/Resources/icons/info_members.png
Normal file
|
After Width: | Height: | Size: 512 B |
BIN
Telegram/Resources/icons/info_members@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/info_notifications.png
Normal file
|
After Width: | Height: | Size: 570 B |
BIN
Telegram/Resources/icons/info_notifications@2x.png
Normal file
|
After Width: | Height: | Size: 1005 B |
BIN
Telegram/Resources/icons/info_recent_actions.png
Normal file
|
After Width: | Height: | Size: 296 B |
BIN
Telegram/Resources/icons/info_recent_actions@2x.png
Normal file
|
After Width: | Height: | Size: 552 B |
BIN
Telegram/Resources/icons/info_restricted_users.png
Normal file
|
After Width: | Height: | Size: 354 B |
BIN
Telegram/Resources/icons/info_restricted_users@2x.png
Normal file
|
After Width: | Height: | Size: 737 B |
BIN
Telegram/Resources/icons/top_bar_call.png
Normal file
|
After Width: | Height: | Size: 436 B |
BIN
Telegram/Resources/icons/top_bar_call@2x.png
Normal file
|
After Width: | Height: | Size: 905 B |
BIN
Telegram/Resources/icons/top_bar_profile.png
Normal file
|
After Width: | Height: | Size: 194 B |
BIN
Telegram/Resources/icons/top_bar_profile@2x.png
Normal file
|
After Width: | Height: | Size: 350 B |
BIN
Telegram/Resources/icons/top_bar_search.png
Normal file
|
After Width: | Height: | Size: 459 B |
BIN
Telegram/Resources/icons/top_bar_search@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Telegram/Resources/icons/upload_chat_photo.png
Normal file
|
After Width: | Height: | Size: 425 B |
BIN
Telegram/Resources/icons/upload_chat_photo@2x.png
Normal file
|
After Width: | Height: | Size: 873 B |
@@ -19,14 +19,13 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
"lng_language_name" = "English";
|
||||
"lng_switch_to_this" = "Switch to English";
|
||||
"lng_switch_to_this" = "Continue in English";
|
||||
|
||||
"lng_menu_contacts" = "Contacts";
|
||||
"lng_menu_calls" = "Calls";
|
||||
"lng_menu_settings" = "Settings";
|
||||
"lng_menu_about" = "About";
|
||||
"lng_menu_update" = "Update";
|
||||
"lng_menu_restart" = "Restart";
|
||||
"lng_menu_back" = "Back";
|
||||
"lng_menu_night_mode" = "Night mode";
|
||||
|
||||
@@ -71,14 +70,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_weekday6" = "Sat";
|
||||
"lng_weekday7" = "Sun";
|
||||
|
||||
"lng_weekday1_full" = "Monday";
|
||||
"lng_weekday2_full" = "Tuesday";
|
||||
"lng_weekday3_full" = "Wednesday";
|
||||
"lng_weekday4_full" = "Thursday";
|
||||
"lng_weekday5_full" = "Friday";
|
||||
"lng_weekday6_full" = "Saturday";
|
||||
"lng_weekday7_full" = "Sunday";
|
||||
|
||||
"lng_month_day" = "{month} {day}";
|
||||
"lng_month_day_year" = "{month} {day}, {year}";
|
||||
"lng_month_year" = "{month} {year}";
|
||||
@@ -104,7 +95,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_status_recently" = "last seen recently";
|
||||
"lng_status_last_week" = "last seen within a week";
|
||||
"lng_status_last_month" = "last seen within a month";
|
||||
"lng_status_invisible" = "invisible";
|
||||
"lng_status_lastseen_now" = "last seen just now";
|
||||
"lng_status_lastseen_minutes#one" = "last seen {count} minute ago";
|
||||
"lng_status_lastseen_minutes#other" = "last seen {count} minutes ago";
|
||||
@@ -118,7 +108,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_status_connecting" = "connecting...";
|
||||
|
||||
"lng_chat_status_unaccessible" = "group is inaccessible";
|
||||
"lng_chat_status_no_members" = "no members";
|
||||
"lng_chat_status_members#one" = "{count} member";
|
||||
"lng_chat_status_members#other" = "{count} members";
|
||||
"lng_chat_status_online#one" = "{count} online";
|
||||
@@ -133,7 +122,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_channel_admins_link#one" = "{count} administrator";
|
||||
"lng_channel_admins_link#other" = "{count} administrators";
|
||||
|
||||
"lng_server_error" = "Internal server error.";
|
||||
"lng_flood_error" = "Too many tries. Please try again later.";
|
||||
"lng_gif_error" = "An error has occurred while reading GIF animation :(";
|
||||
"lng_edit_error" = "You cannot edit this message";
|
||||
@@ -143,7 +131,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_error_pinned_max#one" = "Sorry, you can pin no more than {count} chat to the top.";
|
||||
"lng_error_pinned_max#other" = "Sorry, you can pin no more than {count} chats to the top.";
|
||||
"lng_error_public_groups_denied" = "Unfortunately, you were banned from participating in public groups.\n{more_info}";
|
||||
"lng_error_cant_edit_admin" = "Sorry, you can't edit permissions for this admin.";
|
||||
"lng_error_cant_add_member" = "Sorry, you can't add the bot to this group. Ask a group admin to do it.";
|
||||
"lng_error_cant_add_bot" = "Sorry, this bot can't be added to groups.";
|
||||
"lng_error_cant_add_admin_invite" = "Sorry, you can't add this user as an admin because they are not a member of this group and you are not allowed to invite them.";
|
||||
@@ -179,14 +166,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_photo_caption" = "Caption";
|
||||
"lng_photos_comment" = "Comment";
|
||||
|
||||
"lng_phone_ph" = "Your phone number";
|
||||
"lng_phone_title" = "Your Phone";
|
||||
"lng_phone_desc" = "Please confirm your country code and\nenter your phone number.";
|
||||
"lng_phone_notreg" = "If you don't have a Telegram account yet,\nplease [b]sign up[/b] with {link_start}Android / iPhone{link_end} or {signup_start}here{signup_end}";
|
||||
"lng_country_code" = "Country Code";
|
||||
"lng_bad_country_code" = "Invalid Country Code";
|
||||
"lng_country_ph" = "Search";
|
||||
"lng_country_done" = "Done";
|
||||
"lng_country_none" = "Country not found";
|
||||
"lng_country_select" = "Select country";
|
||||
|
||||
@@ -232,6 +217,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_signin_reset_in_days" = "{days_count} {hours_count} {minutes_count}";
|
||||
"lng_signin_reset_in_hours" = "{hours_count} {minutes_count}";
|
||||
"lng_signin_reset_cancelled" = "Your recent attempts to reset this account have been cancelled by its active user. Please try again in 7 days.";
|
||||
"lng_signin_banned_text" = "This phone number is banned.";
|
||||
"lng_signin_banned_help" = "Help";
|
||||
|
||||
"lng_signup_title" = "Your Info";
|
||||
"lng_signup_desc" = "Please enter your name and\nupload a photo.";
|
||||
@@ -276,7 +263,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_bio_about" = "You can add a few lines about yourself. Anyone who opens your profile will see this text.";
|
||||
|
||||
"lng_settings_section_info" = "Info";
|
||||
"lng_settings_phone_number" = "Phone number:";
|
||||
"lng_settings_username" = "Username:";
|
||||
"lng_settings_choose_username" = "Choose username";
|
||||
"lng_settings_empty_bio" = "None";
|
||||
@@ -316,14 +302,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_workmode_window" = "Show taskbar icon";
|
||||
"lng_settings_auto_start" = "Launch Telegram when system starts";
|
||||
"lng_settings_start_min" = "Launch minimized";
|
||||
"lng_settings_add_sendto" = "Place Telegram in «Send to» menu";
|
||||
"lng_settings_add_sendto" = "Place Telegram in \"Send to\" menu";
|
||||
"lng_settings_section_scale" = "Interface Scale";
|
||||
"lng_settings_scale_auto" = "Auto ({cur})";
|
||||
|
||||
"lng_settings_section_chat_settings" = "Chat Settings";
|
||||
"lng_settings_replace_emojis" = "Replace emoji";
|
||||
"lng_settings_view_emojis" = "View list";
|
||||
"lng_settings_emoji_list" = "Supported emoji";
|
||||
"lng_settings_send_enter" = "Send by Enter";
|
||||
"lng_settings_send_ctrlenter" = "Send by Ctrl+Enter";
|
||||
"lng_settings_send_cmdenter" = "Send by Cmd+Enter";
|
||||
@@ -359,7 +344,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_download_finish_failed" = "File download could not be finished.\n\nWould you like to try again?";
|
||||
"lng_download_path_clearing" = "Clearing...";
|
||||
"lng_download_path_cleared" = "Cleared!";
|
||||
"lng_download_path_clear_failed" = "Clear failed :(";
|
||||
|
||||
"lng_settings_section_privacy" = "Privacy and Security";
|
||||
|
||||
@@ -372,7 +356,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_local_storage_clear" = "Clear all";
|
||||
"lng_local_storage_clearing" = "Clearing...";
|
||||
"lng_local_storage_cleared" = "Cleared!";
|
||||
"lng_local_storage_clear_failed" = "Clear failed :(";
|
||||
|
||||
"lng_settings_section_advanced_settings" = "Advanced Settings";
|
||||
"lng_settings_enable_night_theme" = "Enable night mode";
|
||||
@@ -460,7 +443,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_settings_reset_sure" = "Are you sure you want to terminate\nall other sessions?";
|
||||
"lng_settings_reset_one_sure" = "Do you want to terminate this session?";
|
||||
"lng_settings_reset_button" = "Terminate";
|
||||
"lng_settings_reset_done" = "Other sessions terminated";
|
||||
"lng_settings_manage_local_storage" = "Manage local storage";
|
||||
"lng_settings_ask_question" = "Ask a Question";
|
||||
"lng_settings_ask_sure" = "Please note that Telegram Support is done by volunteers. We try to respond as quickly as possible, but it may take a while.\n\nPlease take a look at the Telegram FAQ: it has important troubleshooting tips and answers to most questions.";
|
||||
@@ -472,11 +454,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_settings_need_restart" = "You need to restart for applying some of the new settings. Restart now?";
|
||||
"lng_settings_restart_now" = "RESTART";
|
||||
"lng_settings_restart_later" = "LATER";
|
||||
|
||||
"lng_sessions_header" = "Current session";
|
||||
"lng_sessions_other_header" = "Active sessions";
|
||||
"lng_sessions_no_other" = "No other sessions";
|
||||
"lng_sessions_other_desc" = "You can log in to Telegram from other mobile, tablet and desktop devices, using the same phone number. All your data will be instantly synchronized.";
|
||||
"lng_sessions_terminate_all" = "Terminate all other sessions";
|
||||
|
||||
@@ -552,17 +532,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_mute_duration_hours#one" = "For {count} hour";
|
||||
"lng_mute_duration_hours#other" = "For {count} hours";
|
||||
"lng_mute_box_tip" = "Choose duration of turning off notifications from the following chat";
|
||||
"lng_mute_box_tip" = "Choose for how long you would like to turn off notifications for the following chat";
|
||||
|
||||
"lng_preview_loading" = "Getting Link Info...";
|
||||
|
||||
"lng_profile_chat_unaccessible" = "Group is inaccessible";
|
||||
"lng_profile_about_section" = "About";
|
||||
"lng_profile_description_section" = "Description";
|
||||
"lng_profile_settings_section" = "Settings";
|
||||
"lng_profile_actions_section" = "Actions";
|
||||
"lng_profile_bot_settings" = "Settings";
|
||||
"lng_profile_bot_help" = "Help";
|
||||
"lng_profile_bot_settings" = "Bot Settings";
|
||||
"lng_profile_bot_help" = "Bot Help";
|
||||
"lng_profile_invite_link_section" = "Invite link";
|
||||
"lng_profile_create_public_link" = "Create public link";
|
||||
"lng_profile_edit_public_link" = "Edit public link";
|
||||
@@ -573,18 +550,13 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_recent_actions" = "Recent actions";
|
||||
"lng_profile_common_groups#one" = "{count} group in common";
|
||||
"lng_profile_common_groups#other" = "{count} groups in common";
|
||||
"lng_profile_common_groups_section" = "Groups in common";
|
||||
"lng_profile_participants_section" = "Members";
|
||||
"lng_profile_info_section" = "Info";
|
||||
"lng_profile_mobile_number" = "Mobile:";
|
||||
"lng_profile_username" = "Username:";
|
||||
"lng_profile_link" = "Link:";
|
||||
"lng_profile_bio" = "Bio:";
|
||||
"lng_profile_add_contact" = "Add Contact";
|
||||
"lng_profile_edit_contact" = "Edit";
|
||||
"lng_profile_enable_notifications" = "Notifications";
|
||||
"lng_profile_clear_history" = "Clear history";
|
||||
"lng_profile_delete_conversation" = "Delete conversation";
|
||||
"lng_profile_clear_and_exit" = "Delete and exit";
|
||||
"lng_profile_leave_channel" = "Leave channel";
|
||||
"lng_profile_delete_channel" = "Delete channel";
|
||||
@@ -592,28 +564,20 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_delete_group" = "Delete group";
|
||||
"lng_profile_report" = "Report";
|
||||
"lng_profile_search_messages" = "Search for messages";
|
||||
"lng_profile_block_user" = "Block user";
|
||||
"lng_profile_unblock_user" = "Unblock user";
|
||||
"lng_profile_block_bot" = "Stop and block bot";
|
||||
"lng_profile_unblock_bot" = "Unblock bot";
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_profile_share_contact" = "Share Contact";
|
||||
"lng_profile_invite_to_group" = "Add to Group";
|
||||
"lng_profile_delete_contact" = "Delete";
|
||||
"lng_profile_set_group_photo" = "Set Photo";
|
||||
"lng_profile_add_participant" = "Add Members";
|
||||
"lng_profile_view_channel" = "View Channel";
|
||||
"lng_profile_join_channel" = "Join";
|
||||
"lng_profile_join_channel" = "Join Channel";
|
||||
"lng_profile_join_group" = "Join Group";
|
||||
"lng_profile_delete_and_exit" = "Leave";
|
||||
"lng_profile_kick" = "Remove";
|
||||
"lng_profile_admin" = "admin";
|
||||
"lng_profile_edit_permissions" = "Edit";
|
||||
"lng_profile_sure_kick" = "Remove {user} from the group?";
|
||||
"lng_profile_sure_kick_channel" = "Remove {user} from the channel?";
|
||||
"lng_profile_sure_kick_admin" = "Remove {user} from administrators?";
|
||||
"lng_profile_loading" = "Loading...";
|
||||
"lng_profile_shared_media" = "Shared media";
|
||||
"lng_profile_no_media" = "No media in this conversation.";
|
||||
"lng_profile_photos#one" = "{count} photo";
|
||||
"lng_profile_photos#other" = "{count} photos";
|
||||
"lng_profile_photos_header" = "Photos";
|
||||
@@ -628,6 +592,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_files_header" = "Files";
|
||||
"lng_profile_audios#one" = "{count} voice message";
|
||||
"lng_profile_audios#other" = "{count} voice messages";
|
||||
//"lng_profile_rounds#one" = "{count} video message";
|
||||
//"lng_profile_rounds#other" = "{count} video messages";
|
||||
"lng_profile_audios_header" = "Voice messages";
|
||||
"lng_profile_shared_links#one" = "{count} shared link";
|
||||
"lng_profile_shared_links#other" = "{count} shared links";
|
||||
@@ -639,6 +605,76 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_drop_area_subtitle_channel" = "to set it as a channel photo";
|
||||
"lng_profile_top_bar_share_contact" = "Share";
|
||||
|
||||
"lng_profile_info_section" = "Info";
|
||||
"lng_info_tab_media" = "Media";
|
||||
"lng_info_mobile_label" = "Mobile";
|
||||
"lng_info_username_label" = "Username";
|
||||
"lng_info_bio_label" = "Bio";
|
||||
"lng_info_link_label" = "Link";
|
||||
"lng_info_about_label" = "About";
|
||||
"lng_info_user_title" = "User Info";
|
||||
"lng_info_bot_title" = "Bot Info";
|
||||
"lng_info_group_title" = "Group Info";
|
||||
"lng_info_channel_title" = "Channel Info";
|
||||
"lng_profile_enable_notifications" = "Notifications";
|
||||
"lng_profile_send_message" = "Send Message";
|
||||
"lng_info_add_as_contact" = "Add as contact";
|
||||
"lng_profile_shared_media" = "Shared media";
|
||||
"lng_media_type_photos" = "Photos";
|
||||
"lng_media_type_videos" = "Videos";
|
||||
"lng_media_type_songs" = "Audio files";
|
||||
"lng_media_type_files" = "Files";
|
||||
"lng_media_type_audios" = "Voice messages";
|
||||
"lng_media_type_links" = "Shared links";
|
||||
"lng_media_type_rounds" = "Video messages";
|
||||
"lng_profile_common_groups_section" = "Groups in common";
|
||||
"lng_profile_share_contact" = "Share Contact";
|
||||
"lng_info_edit_contact" = "Edit contact";
|
||||
"lng_info_delete_contact" = "Delete contact";
|
||||
"lng_info_share_contact" = "Share contact";
|
||||
"lng_profile_clear_history" = "Clear history";
|
||||
"lng_profile_delete_conversation" = "Delete conversation";
|
||||
"lng_profile_block_user" = "Block user";
|
||||
"lng_profile_unblock_user" = "Unblock user";
|
||||
"lng_media_selected_photo#one" = "{count} Photo";
|
||||
"lng_media_selected_photo#other" = "{count} Photos";
|
||||
"lng_media_selected_video#one" = "{count} Video";
|
||||
"lng_media_selected_video#other" = "{count} Videos";
|
||||
"lng_media_selected_song#one" = "{count} Audio file";
|
||||
"lng_media_selected_song#other" = "{count} Audio files";
|
||||
"lng_media_selected_file#one" = "{count} File";
|
||||
"lng_media_selected_file#other" = "{count} Files";
|
||||
"lng_media_selected_audio#one" = "{count} Voice message";
|
||||
"lng_media_selected_audio#other" = "{count} Voice messages";
|
||||
//"lng_media_selected_round#one" = "{count} Video message";
|
||||
//"lng_media_selected_round#other" = "{count} Video messages";
|
||||
"lng_media_selected_link#one" = "{count} Shared link";
|
||||
"lng_media_selected_link#other" = "{count} Shared links";
|
||||
"lng_media_photo_empty" = "No photos here yet";
|
||||
"lng_media_video_empty" = "No videos here yet";
|
||||
"lng_media_song_empty" = "No music files here yet";
|
||||
"lng_media_file_empty" = "No files here yet";
|
||||
"lng_media_audio_empty" = "No voice messages here yet";
|
||||
"lng_media_link_empty" = "No shared links here yet";
|
||||
"lng_media_song_empty_search" = "No music files found";
|
||||
"lng_media_file_empty_search" = "No files found";
|
||||
"lng_media_link_empty_search" = "No shared links found";
|
||||
|
||||
"lng_manage_group_title" = "Manage Group";
|
||||
"lng_manage_channel_title" = "Manage Channel";
|
||||
"lng_manage_group_info" = "Group Info";
|
||||
"lng_manage_channel_info" = "Channel Info";
|
||||
"lng_manage_peer_recent_actions" = "Recent Actions";
|
||||
"lng_manage_peer_members" = "Members";
|
||||
"lng_manage_peer_administrators" = "Administrators";
|
||||
"lng_manage_peer_banned_users" = "Banned users";
|
||||
"lng_manage_peer_restricted_users" = "Restricted users";
|
||||
"lng_manage_history_visibility_title" = "Chat history for new members";
|
||||
"lng_manage_history_visibility_shown" = "Visible";
|
||||
"lng_manage_history_visibility_shown_about" = "New members will see messages that were sent before they joined.";
|
||||
"lng_manage_history_visibility_hidden" = "Hidden";
|
||||
"lng_manage_history_visibility_hidden_about" = "New members won't see earlier messages.";
|
||||
|
||||
"lng_report_title" = "Report channel";
|
||||
"lng_report_group_title" = "Report group";
|
||||
"lng_report_bot_title" = "Report bot";
|
||||
@@ -653,12 +689,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_channel_add_members" = "Add members";
|
||||
"lng_channel_add_banned" = "Ban user";
|
||||
"lng_channel_add_restricted" = "Restrict user";
|
||||
"lng_channel_members" = "Members";
|
||||
"lng_channel_only_last_shown#one" = "Only the last {count} member is shown here";
|
||||
"lng_channel_only_last_shown#other" = "Only the last {count} members are shown here";
|
||||
"lng_channel_admins" = "Administrators";
|
||||
"lng_channel_add_admin" = "Add Administrator";
|
||||
"lng_channel_admins_too_much" = "Sorry, you have reached the limit of the administrators. Please remove one administrator first.";
|
||||
"lng_channel_admin_status_creator" = "Creator";
|
||||
"lng_channel_admin_status_promoted_by" = "Promoted by {user}";
|
||||
"lng_channel_admin_status_not_admin" = "Not administrator";
|
||||
@@ -709,8 +741,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_sure_delete_and_exit" = "Are you sure, you want to delete all message history and leave «{group}»?\n\nThis action cannot be undone.";
|
||||
"lng_sure_leave_channel" = "Are you sure, you want to leave\nthis channel?";
|
||||
"lng_sure_delete_channel" = "Are you sure, you want to delete this channel? All members will be removed and all messages will be lost.";
|
||||
"lng_sure_leave_group" = "Are you sure, you want to leave\nthis group? This action cannot be undone.";
|
||||
"lng_sure_leave_group" = "Are you sure, you want to leave\nthis group?";
|
||||
"lng_sure_delete_group" = "Are you sure, you want to delete this group? All members will be removed and all messages will be lost.";
|
||||
"lng_sure_delete_saved_messages" = "Are you sure, you want to delete all your saved messages?\n\nThis action cannot be undone.";
|
||||
|
||||
"lng_message_empty" = "Empty Message";
|
||||
"lng_message_unsupported" = "This message is not supported by your version of Telegram Desktop. Please update to the last version in Settings or install it from {link}";
|
||||
@@ -730,7 +763,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_action_add_you" = "{from} added you to this channel";
|
||||
"lng_action_you_joined" = "You joined this channel";
|
||||
"lng_action_add_you_group" = "{from} added you to this group";
|
||||
"lng_action_you_joined_group" = "You joined this group";
|
||||
"lng_action_kick_user" = "{from} removed {user}";
|
||||
"lng_action_user_left" = "{from} left the group";
|
||||
"lng_action_user_joined" = "{from} joined the group";
|
||||
@@ -783,17 +815,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_profile_migrate_reached#other" = "{count} members limit reached";
|
||||
"lng_profile_migrate_body" = "To get over this limit, you can upgrade your group to a supergroup.";
|
||||
"lng_profile_migrate_learn_more" = "Learn more »";
|
||||
"lng_profile_migrate_about" = "If you'd like to go over this limit, you can upgrade your group to a supergroup. In supergroups:";
|
||||
"lng_profile_migrate_feature1#one" = "— The members limit is {count} user";
|
||||
"lng_profile_migrate_feature1#other" = "— The members limit is {count} users";
|
||||
"lng_profile_migrate_feature2" = "— New members see the entire chat history";
|
||||
"lng_profile_migrate_feature3" = "— Admins delete messages for everyone";
|
||||
"lng_profile_migrate_feature4" = "— Notifications are muted by default";
|
||||
"lng_profile_migrate_button" = "Upgrade to supergroup";
|
||||
"lng_profile_migrate_sure" = "Are you sure you want to upgrade this group to supergroup? This action cannot be undone.";
|
||||
"lng_profile_convert_button" = "Convert to supergroup";
|
||||
"lng_profile_convert_title" = "Convert to supergroup";
|
||||
"lng_profile_convert_about" = "In supergroups:";
|
||||
"lng_profile_convert_feature1" = "— New members see the full message history";
|
||||
"lng_profile_convert_feature2" = "— Messages are deleted for all members";
|
||||
"lng_profile_convert_feature3" = "— Admins can pin important messages";
|
||||
@@ -812,16 +835,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_channels_too_much_public_revoke" = "Revoke";
|
||||
|
||||
"lng_group_invite_bad_link" = "This invite link is broken or has expired.";
|
||||
"lng_group_invite_want_join" = "Do you want to join the group «{title}»?";
|
||||
"lng_group_invite_want_join_channel" = "Do you want to join the channel «{title}»?";
|
||||
"lng_group_invite_join" = "Join";
|
||||
|
||||
"lng_group_invite_members#one" = "{count} member, among them:";
|
||||
"lng_group_invite_members#other" = "{count} members, among them:";
|
||||
|
||||
"lng_group_invite_link" = "Invite link:";
|
||||
"lng_group_invite_create" = "Create an invite link";
|
||||
"lng_group_invite_about" = "Telegram users will be able to join\nyour group by following this link.";
|
||||
"lng_group_invite_about_channel" = "Telegram users will be able to join\nyour channel by following this link.";
|
||||
"lng_group_invite_create_new" = "Revoke invite link";
|
||||
"lng_group_invite_about_new" = "Your previous link will be deactivated and we'll generate a new invite link for you.";
|
||||
"lng_group_invite_copied" = "Invite link copied to clipboard.";
|
||||
@@ -838,12 +859,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_in_reply_to" = "In reply to";
|
||||
"lng_edited" = "edited";
|
||||
"lng_edited_date" = "Edited: {date}";
|
||||
"lng_admin_badge" = "admin";
|
||||
"lng_cancel_edit_post_sure" = "Cancel editing?";
|
||||
"lng_cancel_edit_post_yes" = "Yes";
|
||||
"lng_cancel_edit_post_no" = "No";
|
||||
|
||||
"lng_bot_share_location_unavailable" = "Sorry, the location sharing is currently unavailable in Telegram Desktop.";
|
||||
"lng_bot_inline_geo_unavailable" = "Sorry, this bot requires location sharing.\nIt is not available in Telegram Desktop.";
|
||||
"lng_bot_share_phone" = "Share Phone Number?";
|
||||
"lng_bot_share_phone_confirm" = "Share";
|
||||
|
||||
@@ -851,14 +872,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_attach_file" = "File";
|
||||
"lng_attach_photo" = "Photo";
|
||||
|
||||
"lng_media_type" = "Media type";
|
||||
"lng_media_type_photos" = "Photos";
|
||||
"lng_media_type_videos" = "Videos";
|
||||
"lng_media_type_songs" = "Audio files";
|
||||
"lng_media_type_files" = "Files";
|
||||
"lng_media_type_audios" = "Voice messages";
|
||||
"lng_media_type_links" = "Shared links";
|
||||
|
||||
"lng_media_open_with" = "Open With";
|
||||
"lng_media_download" = "Download";
|
||||
"lng_media_cancel" = "Cancel";
|
||||
@@ -884,7 +897,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_emoji_hide_panel" = "Click here to hide the emoji sidebar";
|
||||
|
||||
"lng_recent_stickers" = "Frequently used";
|
||||
"lng_faved_stickers" = "Favorite stickers";
|
||||
"lng_faved_stickers_add" = "Add to Favorites";
|
||||
"lng_faved_stickers_remove" = "Remove from Favorites";
|
||||
"lng_group_stickers" = "Group stickers";
|
||||
@@ -911,11 +923,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_stickers_share_pack" = "Share Stickers";
|
||||
"lng_stickers_not_found" = "Sticker pack not found.";
|
||||
"lng_stickers_packs_archived" = "Some of your unused stickers have been archived to make room for the sets you've activated.";
|
||||
"lng_stickers_archived" = "Archived Stickers";
|
||||
"lng_stickers_copied" = "Sticker pack link copied to clipboard.";
|
||||
"lng_stickers_default_set" = "Great Minds";
|
||||
"lng_stickers_you_have" = "Manage and reorder sticker packs";
|
||||
"lng_stickers_featured" = "Trending Stickers";
|
||||
"lng_stickers_return" = "Undo";
|
||||
"lng_stickers_count#one" = "{count} sticker";
|
||||
"lng_stickers_count#other" = "{count} stickers";
|
||||
@@ -923,6 +933,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_stickers_group_set" = "Group sticker set";
|
||||
"lng_stickers_remove_group_set" = "Remove group sticker set?";
|
||||
"lng_stickers_group_from_your" = "Choose from your stickers";
|
||||
"lng_stickers_group_from_featured" = "Choose from trending stickers";
|
||||
|
||||
"lng_in_dlg_photo" = "Photo";
|
||||
"lng_in_dlg_video" = "Video";
|
||||
@@ -945,16 +956,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_report_spam_ok" = "Report";
|
||||
"lng_cant_send_to_not_contact" = "Sorry, you can only send messages to\nmutual contacts at the moment.\n{more_info}";
|
||||
"lng_cant_invite_not_contact" = "Sorry, you can only add mutual contacts\nto groups at the moment.\n{more_info}";
|
||||
"lng_cant_send_to_not_contact_flood" = "You have contacted too many non-contacts today, please try again tomorrow. You will be able to reply today if this user messages you first.";
|
||||
"lng_cant_invite_not_contact_flood" = "You can't add this user because you have contacted too many non-contacts today. Please try again tomorrow. You can ask another member to add this user to the group.";
|
||||
"lng_cant_more_info" = "More info »";
|
||||
"lng_cant_invite_banned" = "Sorry, only admin can add this user.";
|
||||
"lng_cant_invite_privacy" = "Sorry, you cannot add this user to groups because of their privacy settings.";
|
||||
"lng_cant_invite_privacy_channel" = "Sorry, you cannot add this user to channels because of their privacy settings.";
|
||||
"lng_cant_delete_group#one" = "Sorry, it is currently not possible to manually delete communities that have more than {count} member. Please contact Telegram support if you would like to delete this group.";
|
||||
"lng_cant_delete_group#other" = "Sorry, it is currently not possible to manually delete communities that have more than {count} members. Please contact Telegram support if you would like to delete this group.";
|
||||
"lng_cant_delete_channel#one" = "Sorry, it is currently not possible to manually delete communities that have more than {count} member. Please contact Telegram support if you would like to delete this channel.";
|
||||
"lng_cant_delete_channel#other" = "Sorry, it is currently not possible to manually delete communities that have more than {count} members. Please contact Telegram support if you would like to delete this channel.";
|
||||
"lng_cant_do_this" = "Sorry, this action is unavailable.";
|
||||
|
||||
"lng_send_button" = "Send";
|
||||
@@ -969,9 +974,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_from_draft" = "Draft";
|
||||
"lng_bot_description" = "What can this bot do?";
|
||||
"lng_unblock_button" = "Unblock";
|
||||
"lng_channel_join" = "Join Channel";
|
||||
"lng_channel_mute" = "Mute";
|
||||
"lng_channel_unmute" = "Unmute";
|
||||
"lng_saved_messages" = "Saved Messages";
|
||||
"lng_saved_short" = "Save";
|
||||
"lng_saved_forward_here" = "Forward messages here for quick access";
|
||||
|
||||
"lng_dialogs_text_with_from" = "{from_part} {message}";
|
||||
"lng_dialogs_text_from_wrapped" = "{from}:";
|
||||
@@ -1023,10 +1030,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_user_action_upload_photo" = "{user} is sending a photo";
|
||||
"lng_send_action_upload_file" = "sending a file";
|
||||
"lng_user_action_upload_file" = "{user} is sending a file";
|
||||
"lng_send_action_geo_location" = "picking a location";
|
||||
"lng_user_action_geo_location" = "{user} is picking a location";
|
||||
"lng_send_action_choose_contact" = "choosing a contact";
|
||||
"lng_user_action_choose_contact" = "{user} is choosing a contact";
|
||||
"lng_unread_bar#one" = "{count} unread message";
|
||||
"lng_unread_bar#other" = "{count} unread messages";
|
||||
|
||||
@@ -1061,10 +1064,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_context_copy_hashtag" = "Copy Hashtag";
|
||||
"lng_context_copy_mention" = "Copy Username";
|
||||
"lng_context_save_image" = "Save Image As...";
|
||||
"lng_context_forward_image" = "Forward Image";
|
||||
"lng_context_delete_image" = "Delete Image";
|
||||
"lng_context_copy_image" = "Copy Image";
|
||||
"lng_context_close_image" = "Close Image";
|
||||
"lng_context_cancel_download" = "Cancel Download";
|
||||
"lng_context_show_in_folder" = "Show in Folder";
|
||||
"lng_context_show_in_finder" = "Show in Finder";
|
||||
@@ -1074,9 +1074,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_context_pack_info" = "Pack Info";
|
||||
"lng_context_pack_add" = "Add Stickers";
|
||||
"lng_context_save_file" = "Save File As...";
|
||||
"lng_context_forward_file" = "Forward File";
|
||||
"lng_context_delete_file" = "Delete File";
|
||||
"lng_context_close_file" = "Close File";
|
||||
"lng_context_copy_text" = "Copy Text";
|
||||
"lng_context_open_gif" = "Open GIF";
|
||||
"lng_context_save_gif" = "Save GIF";
|
||||
@@ -1094,15 +1091,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_context_forward_selected" = "Forward Selected";
|
||||
"lng_context_delete_selected" = "Delete Selected";
|
||||
"lng_context_clear_selection" = "Clear Selection";
|
||||
"lng_really_send_image" = "Do you want to send this image?";
|
||||
"lng_really_send_file" = "Do you want to send this file?";
|
||||
"lng_really_share_contact" = "Do you want to share this contact?";
|
||||
"lng_send_images_compress#one" = "Compress image";
|
||||
"lng_send_images_compress#other" = "Compress images";
|
||||
"lng_send_image_non_local" = "Could not send a non local file: {name}";
|
||||
"lng_send_image_empty" = "Could not send an empty file: {name}";
|
||||
"lng_send_image_too_large" = "Could not send a file, because it is larger than 1500 MB: {name}";
|
||||
"lng_send_folder" = "Could not send «{name}» because it is a directory :(";
|
||||
"lng_send_images_selected#one" = "{count} image selected";
|
||||
"lng_send_images_selected#other" = "{count} images selected";
|
||||
"lng_send_photos#one" = "Send {count} photo";
|
||||
@@ -1114,11 +1106,10 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_forward_choose" = "Choose recipient...";
|
||||
"lng_forward_cant" = "Sorry, no way to forward here :(";
|
||||
"lng_forward_confirm" = "Forward to {recipient}?";
|
||||
"lng_forward_share_contact" = "Share contact to {recipient}?";
|
||||
"lng_forward_share_cant" = "Sorry, no way to share contact here :(";
|
||||
"lng_forward_send_file_confirm" = "Send «{name}» to {recipient}?";
|
||||
"lng_forward_send_files_confirm" = "Send selected files to {recipient}?";
|
||||
|
||||
|
||||
"lng_forward_send_files_cant" = "Sorry, no way to send media here :(";
|
||||
"lng_forward_send" = "Send";
|
||||
"lng_forward_messages#one" = "{count} forwarded message";
|
||||
@@ -1142,7 +1133,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_contact_phone" = "Phone number";
|
||||
"lng_enter_contact_data" = "New Contact";
|
||||
"lng_edit_group_title" = "Edit group name";
|
||||
"lng_edit_contact_title" = "Edit contact name";
|
||||
"lng_edit_channel_title" = "Edit channel";
|
||||
"lng_edit_sign_messages" = "Sign messages";
|
||||
@@ -1159,7 +1149,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_try_other_contact" = "Try other";
|
||||
"lng_create_group_link" = "Link";
|
||||
"lng_create_group_invite_link" = "Invite link";
|
||||
"lng_create_group_photo" = "Set Photo";
|
||||
"lng_create_group_description" = "Description (optional)";
|
||||
|
||||
"lng_drag_images_here" = "Drop images here";
|
||||
@@ -1173,8 +1162,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_selected_clear" = "Cancel";
|
||||
"lng_selected_delete" = "Delete";
|
||||
"lng_selected_forward" = "Forward";
|
||||
"lng_selected_count#one" = "{count} message";
|
||||
"lng_selected_count#other" = "{count} messages";
|
||||
"lng_selected_cancel_sure_this" = "Cancel uploading?";
|
||||
"lng_selected_upload_stop" = "Stop";
|
||||
"lng_selected_delete_sure_this" = "Do you want to delete this message?";
|
||||
@@ -1233,7 +1220,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
"lng_new_version_wrap" = "Telegram Desktop was updated to version {version}\n\n{changes}\n\nFull version history is available here:\n{link}";
|
||||
"lng_new_version_minor" = "— Bug fixes and other minor improvements";
|
||||
"lng_new_version_text" = "— Improved video messages: radial playback progress, Picture-in-Picture support, duration countdown.\n— Voice and video messages now automatically play one after another.";
|
||||
|
||||
"lng_menu_insert_unicode" = "Insert Unicode control character";
|
||||
|
||||
@@ -1282,7 +1268,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_call_fingerprint_tooltip" = "If emoji on {user}'s screen are the same, this call is 100% secure";
|
||||
|
||||
"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
|
||||
"lng_call_error_incompatible" = "{user}'s app is using an incompatible protocol. They need to update their app before you can call them.";
|
||||
"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
|
||||
"lng_call_error_audio_io" = "There seems to be a problem with audio playback on your computer. Please make sure that your computer's speakers and microphone are working and try again.";
|
||||
|
||||
@@ -1342,7 +1327,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_rights_chat_banned_week#other" = "For {count} weeks";
|
||||
"lng_rights_chat_banned_custom" = "Custom";
|
||||
"lng_rights_chat_banned_custom_date" = "Until {date}";
|
||||
"lng_rights_chat_banned_block" = "Ban and remove from group";
|
||||
|
||||
"lng_restricted_send_message" = "The admins of this group restricted you from writing here.";
|
||||
"lng_restricted_send_media" = "The admins of this group restricted you from posting media content here.";
|
||||
@@ -1397,6 +1381,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_admin_log_invites_disabled" = "{from} disabled group invites";
|
||||
"lng_admin_log_signatures_enabled" = "{from} enabled signatures";
|
||||
"lng_admin_log_signatures_disabled" = "{from} disabled signatures";
|
||||
"lng_admin_log_history_made_hidden" = "{from} made group history hidden for new members";
|
||||
"lng_admin_log_history_made_visible" = "{from} made group history visible for new members";
|
||||
"lng_admin_log_pinned_message" = "{from} pinned message:";
|
||||
"lng_admin_log_unpinned_message" = "{from} unpinned message";
|
||||
"lng_admin_log_edited_caption" = "{from} edited caption:";
|
||||
@@ -1434,15 +1420,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
"lng_admin_log_admin_pin_messages" = "Pin messages";
|
||||
"lng_admin_log_admin_add_admins" = "Add new admins";
|
||||
|
||||
// Not used
|
||||
|
||||
"lng_topbar_info" = "Info";
|
||||
"lng_profile_group_info" = "Group info";
|
||||
"lng_profile_channel_info" = "Channel info";
|
||||
"lng_channel_add_admins" = "New administrator";
|
||||
"lng_dlg_search_chat" = "Search in this chat";
|
||||
"lng_dlg_search_channel" = "Search in this channel";
|
||||
|
||||
// Wnd specific
|
||||
|
||||
"lng_wnd_choose_program_menu" = "Choose Default Program...";
|
||||
|
||||
30
Telegram/Resources/langs/refresh.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import os, sys, requests, re
|
||||
from os.path import expanduser
|
||||
|
||||
filename = ''
|
||||
for arg in sys.argv:
|
||||
if re.match(r'.*\.strings$', arg):
|
||||
filename = expanduser(arg)
|
||||
|
||||
result = ''
|
||||
if not os.path.isfile(filename):
|
||||
print("File not found.")
|
||||
sys.exit(1)
|
||||
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
if re.match(r'\s*\/\* new from [a-zA-Z0-9\.]+ \*\/\s*', line):
|
||||
continue
|
||||
if re.match(r'\"lng_[a-z_]+\#(zero|two|few|many)\".+', line):
|
||||
continue
|
||||
result = result + line
|
||||
|
||||
remove = 0
|
||||
while (len(result) > remove + 1) and (result[len(result) - remove - 1] == '\n') and (result[len(result) - remove - 2] == '\n'):
|
||||
remove = remove + 1
|
||||
result = result[:len(result) - remove]
|
||||
|
||||
with open('lang.strings', 'w') as out:
|
||||
out.write(result)
|
||||
|
||||
sys.exit()
|
||||
30
Telegram/Resources/langs/refresh.sh
Executable file
@@ -0,0 +1,30 @@
|
||||
set -e
|
||||
FullExecPath=$PWD
|
||||
pushd `dirname $0` > /dev/null
|
||||
FullScriptPath=`pwd`
|
||||
popd > /dev/null
|
||||
|
||||
FileName="$1"
|
||||
|
||||
if [ ! -d "$FullScriptPath/../../../../TelegramPrivate" ]; then
|
||||
echo ""
|
||||
echo "This script is for building the production version of Telegram Desktop."
|
||||
echo ""
|
||||
echo "For building custom versions please visit the build instructions page at:"
|
||||
echo "https://github.com/telegramdesktop/tdesktop/#build-instructions"
|
||||
exit
|
||||
fi
|
||||
|
||||
Error () {
|
||||
cd $FullExecPath
|
||||
echo "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [ ! -f "$FileName" ]; then
|
||||
Error "File '$FileName' not found."
|
||||
fi
|
||||
|
||||
cd "$FullScriptPath"
|
||||
python refresh.py $FileName
|
||||
|
||||
@@ -159,14 +159,15 @@ inputMediaUploadedPhoto#2f37e231 flags:# file:InputFile caption:string stickers:
|
||||
inputMediaPhoto#81fa373a flags:# id:InputPhoto caption:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGeoPoint#f9c44144 geo_point:InputGeoPoint = InputMedia;
|
||||
inputMediaContact#a6e45987 phone_number:string first_name:string last_name:string = InputMedia;
|
||||
inputMediaUploadedDocument#e39621fd flags:# file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaUploadedDocument#e39621fd flags:# nosound_video:flags.3?true file:InputFile thumb:flags.2?InputFile mime_type:string attributes:Vector<DocumentAttribute> caption:string stickers:flags.0?Vector<InputDocument> ttl_seconds:flags.1?int = InputMedia;
|
||||
inputMediaDocument#5acb668e flags:# id:InputDocument caption:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaVenue#2827a81a geo_point:InputGeoPoint title:string address:string provider:string venue_id:string = InputMedia;
|
||||
inputMediaVenue#c13d1c11 geo_point:InputGeoPoint title:string address:string provider:string venue_id:string venue_type:string = InputMedia;
|
||||
inputMediaGifExternal#4843b0fd url:string q:string = InputMedia;
|
||||
inputMediaPhotoExternal#922aec1 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaDocumentExternal#b6f74335 flags:# url:string caption:string ttl_seconds:flags.0?int = InputMedia;
|
||||
inputMediaGame#d33f43f3 id:InputGame = InputMedia;
|
||||
inputMediaInvoice#92153685 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string start_param:string = InputMedia;
|
||||
inputMediaInvoice#f4e096c3 flags:# title:string description:string photo:flags.0?InputWebDocument invoice:Invoice payload:bytes provider:string provider_data:DataJSON start_param:string = InputMedia;
|
||||
inputMediaGeoLive#7b1a118f geo_point:InputGeoPoint period:int = InputMedia;
|
||||
|
||||
inputChatPhotoEmpty#1ca48f57 = InputChatPhoto;
|
||||
inputChatUploadedPhoto#927c55b4 file:InputFile = InputChatPhoto;
|
||||
@@ -218,11 +219,11 @@ userStatusLastMonth#77ebc742 = UserStatus;
|
||||
chatEmpty#9ba2d800 id:int = Chat;
|
||||
chat#d91cdd54 flags:# creator:flags.0?true kicked:flags.1?true left:flags.2?true admins_enabled:flags.3?true admin:flags.4?true deactivated:flags.5?true id:int title:string photo:ChatPhoto participants_count:int date:int version:int migrated_to:flags.6?InputChannel = Chat;
|
||||
chatForbidden#7328bdb id:int title:string = Chat;
|
||||
channel#cb44b1c flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights = Chat;
|
||||
channel#450b7115 flags:# creator:flags.0?true left:flags.2?true editor:flags.3?true broadcast:flags.5?true verified:flags.7?true megagroup:flags.8?true restricted:flags.9?true democracy:flags.10?true signatures:flags.11?true min:flags.12?true id:int access_hash:flags.13?long title:string username:flags.6?string photo:ChatPhoto date:int version:int restriction_reason:flags.9?string admin_rights:flags.14?ChannelAdminRights banned_rights:flags.15?ChannelBannedRights participants_count:flags.17?int = Chat;
|
||||
channelForbidden#289da732 flags:# broadcast:flags.5?true megagroup:flags.8?true id:int access_hash:long title:string until_date:flags.16?int = Chat;
|
||||
|
||||
chatFull#2e02a614 id:int participants:ChatParticipants chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> = ChatFull;
|
||||
channelFull#17f45fcf flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet = ChatFull;
|
||||
channelFull#76af5481 flags:# can_view_participants:flags.3?true can_set_username:flags.6?true can_set_stickers:flags.7?true hidden_prehistory:flags.10?true id:int about:string participants_count:flags.0?int admins_count:flags.1?int kicked_count:flags.2?int banned_count:flags.2?int read_inbox_max_id:int read_outbox_max_id:int unread_count:int chat_photo:Photo notify_settings:PeerNotifySettings exported_invite:ExportedChatInvite bot_info:Vector<BotInfo> migrated_from_chat_id:flags.4?int migrated_from_max_id:flags.4?int pinned_msg_id:flags.5?int stickerset:flags.8?StickerSet available_min_id:flags.9?int = ChatFull;
|
||||
|
||||
chatParticipant#c8d7493e user_id:int inviter_id:int date:int = ChatParticipant;
|
||||
chatParticipantCreator#da13538a user_id:int = ChatParticipant;
|
||||
@@ -235,7 +236,7 @@ chatPhotoEmpty#37c1011c = ChatPhoto;
|
||||
chatPhoto#6153276a photo_small:FileLocation photo_big:FileLocation = ChatPhoto;
|
||||
|
||||
messageEmpty#83e5de54 id:int = Message;
|
||||
message#90dddc11 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string = Message;
|
||||
message#44f9b43d flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer fwd_from:flags.2?MessageFwdHeader via_bot_id:flags.11?int reply_to_msg_id:flags.3?int date:int message:string media:flags.9?MessageMedia reply_markup:flags.6?ReplyMarkup entities:flags.7?Vector<MessageEntity> views:flags.10?int edit_date:flags.15?int post_author:flags.16?string grouped_id:flags.17?long = Message;
|
||||
messageService#9e19a1f6 flags:# out:flags.1?true mentioned:flags.4?true media_unread:flags.5?true silent:flags.13?true post:flags.14?true id:int from_id:flags.8?int to_id:Peer reply_to_msg_id:flags.3?int date:int action:MessageAction = Message;
|
||||
|
||||
messageMediaEmpty#3ded6320 = MessageMedia;
|
||||
@@ -245,9 +246,10 @@ messageMediaContact#5e7d2f39 phone_number:string first_name:string last_name:str
|
||||
messageMediaUnsupported#9f84f49e = MessageMedia;
|
||||
messageMediaDocument#7c4414d3 flags:# document:flags.0?Document caption:flags.1?string ttl_seconds:flags.2?int = MessageMedia;
|
||||
messageMediaWebPage#a32dd600 webpage:WebPage = MessageMedia;
|
||||
messageMediaVenue#7912b71f geo:GeoPoint title:string address:string provider:string venue_id:string = MessageMedia;
|
||||
messageMediaVenue#2ec0533f geo:GeoPoint title:string address:string provider:string venue_id:string venue_type:string = MessageMedia;
|
||||
messageMediaGame#fdb19008 game:Game = MessageMedia;
|
||||
messageMediaInvoice#84551347 flags:# shipping_address_requested:flags.1?true test:flags.3?true title:string description:string photo:flags.0?WebDocument receipt_msg_id:flags.2?int currency:string total_amount:long start_param:string = MessageMedia;
|
||||
messageMediaGeoLive#7c3c2609 geo:GeoPoint period:int = MessageMedia;
|
||||
|
||||
messageActionEmpty#b6aef7b0 = MessageAction;
|
||||
messageActionChatCreate#a6638b9a title:string users:Vector<int> = MessageAction;
|
||||
@@ -267,6 +269,7 @@ messageActionPaymentSentMe#8f31b327 flags:# currency:string total_amount:long pa
|
||||
messageActionPaymentSent#40699cd0 currency:string total_amount:long = MessageAction;
|
||||
messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDiscardReason duration:flags.1?int = MessageAction;
|
||||
messageActionScreenshotTaken#4792929b = MessageAction;
|
||||
messageActionCustomAction#fae69f56 message:string = MessageAction;
|
||||
|
||||
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
||||
|
||||
@@ -340,6 +343,7 @@ messages.dialogsSlice#71e094f3 count:int dialogs:Vector<Dialog> messages:Vector<
|
||||
messages.messages#8c718e87 messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesSlice#b446ae3 count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.channelMessages#99262e37 flags:# pts:int count:int messages:Vector<Message> chats:Vector<Chat> users:Vector<User> = messages.Messages;
|
||||
messages.messagesNotModified#74535f21 count:int = messages.Messages;
|
||||
|
||||
messages.chats#64ff9fd5 chats:Vector<Chat> = messages.Chats;
|
||||
messages.chatsSlice#9cd81144 count:int chats:Vector<Chat> = messages.Chats;
|
||||
@@ -352,7 +356,6 @@ inputMessagesFilterEmpty#57e2f66c = MessagesFilter;
|
||||
inputMessagesFilterPhotos#9609a51c = MessagesFilter;
|
||||
inputMessagesFilterVideo#9fc00e65 = MessagesFilter;
|
||||
inputMessagesFilterPhotoVideo#56e9f0e4 = MessagesFilter;
|
||||
inputMessagesFilterPhotoVideoDocuments#d95e73bb = MessagesFilter;
|
||||
inputMessagesFilterDocument#9eddf188 = MessagesFilter;
|
||||
inputMessagesFilterUrl#7ef0dd87 = MessagesFilter;
|
||||
inputMessagesFilterGif#ffc86587 = MessagesFilter;
|
||||
@@ -363,6 +366,8 @@ inputMessagesFilterPhoneCalls#80c99768 flags:# missed:flags.0?true = MessagesFil
|
||||
inputMessagesFilterRoundVoice#7a7c17a4 = MessagesFilter;
|
||||
inputMessagesFilterRoundVideo#b549da53 = MessagesFilter;
|
||||
inputMessagesFilterMyMentions#c1f8e69a = MessagesFilter;
|
||||
inputMessagesFilterGeo#e7026d0d = MessagesFilter;
|
||||
inputMessagesFilterContacts#e062db83 = MessagesFilter;
|
||||
|
||||
updateNewMessage#1f2b0afd message:Message pts:int pts_count:int = Update;
|
||||
updateMessageID#4e90bfd6 id:int random_id:long = Update;
|
||||
@@ -429,6 +434,7 @@ updateLangPack#56022f4d difference:LangPackDifference = Update;
|
||||
updateFavedStickers#e511996d = Update;
|
||||
updateChannelReadMessagesContents#89893b45 channel_id:int messages:Vector<int> = Update;
|
||||
updateContactsReset#7084a7be = Update;
|
||||
updateChannelAvailableMessages#70db6837 channel_id:int available_min_id:int = Update;
|
||||
|
||||
updates.state#a56c2a3e pts:int qts:int date:int seq:int unread_count:int = updates.State;
|
||||
|
||||
@@ -455,7 +461,7 @@ upload.fileCdnRedirect#ea52fe5a dc_id:int file_token:bytes encryption_key:bytes
|
||||
|
||||
dcOption#5d8c6cc flags:# ipv6:flags.0?true media_only:flags.1?true tcpo_only:flags.2?true cdn:flags.3?true static:flags.4?true id:int ip_address:string port:int = DcOption;
|
||||
|
||||
config#8df376a4 flags:# phonecalls_enabled:flags.1?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector<DisabledFeature> = Config;
|
||||
config#9c840964 flags:# phonecalls_enabled:flags.1?true default_p2p_contacts:flags.3?true date:int expires:int test_mode:Bool this_dc:int dc_options:Vector<DcOption> chat_size_max:int megagroup_size_max:int forwarded_count_max:int online_update_period_ms:int offline_blur_timeout_ms:int offline_idle_timeout_ms:int online_cloud_timeout_ms:int notify_cloud_delay_ms:int notify_default_delay_ms:int chat_big_size:int push_chat_period_ms:int push_chat_limit:int saved_gifs_limit:int edit_time_limit:int rating_e_decay:int stickers_recent_limit:int stickers_faved_limit:int channels_read_media_period:int tmp_sessions:flags.0?int pinned_dialogs_count_max:int call_receive_timeout_ms:int call_ring_timeout_ms:int call_connect_timeout_ms:int call_packet_timeout_ms:int me_url_prefix:string suggested_lang_code:flags.2?string lang_pack_version:flags.2?int disabled_features:Vector<DisabledFeature> = Config;
|
||||
|
||||
nearestDc#8e1a1775 country:string this_dc:int nearest_dc:int = NearestDc;
|
||||
|
||||
@@ -665,6 +671,7 @@ channelParticipantsBanned#1427a5e1 q:string = ChannelParticipantsFilter;
|
||||
channelParticipantsSearch#656ac4b q:string = ChannelParticipantsFilter;
|
||||
|
||||
channels.channelParticipants#f56ee2a8 count:int participants:Vector<ChannelParticipant> users:Vector<User> = channels.ChannelParticipants;
|
||||
channels.channelParticipantsNotModified#f0173fe9 = channels.ChannelParticipants;
|
||||
|
||||
channels.channelParticipant#d0d9b163 participant:ChannelParticipant users:Vector<User> = channels.ChannelParticipant;
|
||||
|
||||
@@ -680,7 +687,7 @@ messages.savedGifs#2e0709a5 hash:int gifs:Vector<Document> = messages.SavedGifs;
|
||||
|
||||
inputBotInlineMessageMediaAuto#292fed13 flags:# caption:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageText#3dcd7a87 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaGeo#f4a59de1 flags:# geo_point:InputGeoPoint reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaGeo#c1b15d65 flags:# geo_point:InputGeoPoint period:int reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaVenue#aaafadc8 flags:# geo_point:InputGeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageMediaContact#2daf01a7 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
inputBotInlineMessageGame#4b425864 flags:# reply_markup:flags.2?ReplyMarkup = InputBotInlineMessage;
|
||||
@@ -692,18 +699,18 @@ inputBotInlineResultGame#4fa417f2 id:string short_name:string send_message:Input
|
||||
|
||||
botInlineMessageMediaAuto#a74b15b flags:# caption:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageText#8c7f65e2 flags:# no_webpage:flags.0?true message:string entities:flags.1?Vector<MessageEntity> reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaGeo#3a8fd8b8 flags:# geo:GeoPoint reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaGeo#b722de65 flags:# geo:GeoPoint period:int reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaVenue#4366232e flags:# geo:GeoPoint title:string address:string provider:string venue_id:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
botInlineMessageMediaContact#35edb4d4 flags:# phone_number:string first_name:string last_name:string reply_markup:flags.2?ReplyMarkup = BotInlineMessage;
|
||||
|
||||
botInlineResult#9bebaeb9 flags:# id:string type:string title:flags.1?string description:flags.2?string url:flags.3?string thumb_url:flags.4?string content_url:flags.5?string content_type:flags.5?string w:flags.6?int h:flags.6?int duration:flags.7?int send_message:BotInlineMessage = BotInlineResult;
|
||||
botInlineMediaResult#17db940b flags:# id:string type:string photo:flags.0?Photo document:flags.1?Document title:flags.2?string description:flags.3?string send_message:BotInlineMessage = BotInlineResult;
|
||||
|
||||
messages.botResults#ccd3563d flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int = messages.BotResults;
|
||||
messages.botResults#947ca848 flags:# gallery:flags.0?true query_id:long next_offset:flags.1?string switch_pm:flags.2?InlineBotSwitchPM results:Vector<BotInlineResult> cache_time:int users:Vector<User> = messages.BotResults;
|
||||
|
||||
exportedMessageLink#1f486803 link:string = ExportedMessageLink;
|
||||
|
||||
messageFwdHeader#fadff4ac flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string = MessageFwdHeader;
|
||||
messageFwdHeader#559ebe6d flags:# from_id:flags.0?int date:int channel_id:flags.1?int channel_post:flags.2?int post_author:flags.3?string saved_from_peer:flags.4?Peer saved_from_msg_id:flags.4?int = MessageFwdHeader;
|
||||
|
||||
auth.codeTypeSms#72a3158c = auth.CodeType;
|
||||
auth.codeTypeCall#741cd3e3 = auth.CodeType;
|
||||
@@ -714,7 +721,7 @@ auth.sentCodeTypeSms#c000bba2 length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeCall#5353e5a7 length:int = auth.SentCodeType;
|
||||
auth.sentCodeTypeFlashCall#ab03c6d9 pattern:string = auth.SentCodeType;
|
||||
|
||||
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
|
||||
messages.botCallbackAnswer#36585ea4 flags:# alert:flags.1?true has_url:flags.3?true native_ui:flags.4?true message:flags.0?string url:flags.2?string cache_time:int = messages.BotCallbackAnswer;
|
||||
|
||||
messages.messageEditData#26b5dde6 flags:# caption:flags.0?true = messages.MessageEditData;
|
||||
|
||||
@@ -816,7 +823,7 @@ dataJSON#7d748d04 data:string = DataJSON;
|
||||
|
||||
labeledPrice#cb296bf8 label:string amount:long = LabeledPrice;
|
||||
|
||||
invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true currency:string prices:Vector<LabeledPrice> = Invoice;
|
||||
invoice#c30aa358 flags:# test:flags.0?true name_requested:flags.1?true phone_requested:flags.2?true email_requested:flags.3?true shipping_address_requested:flags.4?true flexible:flags.5?true phone_to_provider:flags.6?true email_to_provider:flags.7?true currency:string prices:Vector<LabeledPrice> = Invoice;
|
||||
|
||||
paymentCharge#ea02c27e id:string provider_charge_id:string = PaymentCharge;
|
||||
|
||||
@@ -847,6 +854,8 @@ payments.savedInfo#fb8fe43c flags:# has_saved_credentials:flags.1?true saved_inf
|
||||
|
||||
inputPaymentCredentialsSaved#c10eb2cf id:string tmp_password:bytes = InputPaymentCredentials;
|
||||
inputPaymentCredentials#3417d728 flags:# save:flags.0?true data:DataJSON = InputPaymentCredentials;
|
||||
inputPaymentCredentialsApplePay#aa1c39f payment_data:DataJSON = InputPaymentCredentials;
|
||||
inputPaymentCredentialsAndroidPay#795667a6 payment_token:DataJSON = InputPaymentCredentials;
|
||||
|
||||
account.tmpPassword#db64fd34 tmp_password:bytes valid_until:int = account.TmpPassword;
|
||||
|
||||
@@ -903,6 +912,7 @@ channelAdminLogEventActionParticipantInvite#e31c34d8 participant:ChannelParticip
|
||||
channelAdminLogEventActionParticipantToggleBan#e6d83d7e prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionParticipantToggleAdmin#d5676710 prev_participant:ChannelParticipant new_participant:ChannelParticipant = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionChangeStickerSet#b1c3caa7 prev_stickerset:InputStickerSet new_stickerset:InputStickerSet = ChannelAdminLogEventAction;
|
||||
channelAdminLogEventActionTogglePreHistoryHidden#5f5c95f1 new_value:Bool = ChannelAdminLogEventAction;
|
||||
|
||||
channelAdminLogEvent#3b5a3e40 id:long date:int user_id:int action:ChannelAdminLogEventAction = ChannelAdminLogEvent;
|
||||
|
||||
@@ -917,6 +927,16 @@ cdnFileHash#77eec38f offset:int limit:int hash:bytes = CdnFileHash;
|
||||
messages.favedStickersNotModified#9e8fa6d3 = messages.FavedStickers;
|
||||
messages.favedStickers#f37f2f16 hash:int packs:Vector<StickerPack> stickers:Vector<Document> = messages.FavedStickers;
|
||||
|
||||
recentMeUrlUnknown#46e1d13d url:string = RecentMeUrl;
|
||||
recentMeUrlUser#8dbc3336 url:string user_id:int = RecentMeUrl;
|
||||
recentMeUrlChat#a01b22f9 url:string chat_id:int = RecentMeUrl;
|
||||
recentMeUrlChatInvite#eb49081d url:string chat_invite:ChatInvite = RecentMeUrl;
|
||||
recentMeUrlStickerSet#bc0a57dc url:string set:StickerSetCovered = RecentMeUrl;
|
||||
|
||||
help.recentMeUrls#e0310d7 urls:Vector<RecentMeUrl> chats:Vector<Chat> users:Vector<User> = help.RecentMeUrls;
|
||||
|
||||
inputSingleMedia#5eaa7809 media:InputMedia random_id:long = InputSingleMedia;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@@ -992,7 +1012,7 @@ contacts.resetSaved#879537f1 = Bool;
|
||||
|
||||
messages.getMessages#4222fa74 id:Vector<int> = messages.Messages;
|
||||
messages.getDialogs#191ba9c5 flags:# exclude_pinned:flags.0?true offset_date:int offset_id:int offset_peer:InputPeer limit:int = messages.Dialogs;
|
||||
messages.getHistory#afa92846 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.getHistory#dcbb8260 peer:InputPeer offset_id:int offset_date:int add_offset:int limit:int max_id:int min_id:int hash:int = messages.Messages;
|
||||
messages.search#39e9ea0 flags:# peer:InputPeer q:string from_id:flags.0?InputUser filter:MessagesFilter min_date:int max_date:int offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readHistory#e306d3a peer:InputPeer max_id:int = messages.AffectedMessages;
|
||||
messages.deleteHistory#1c015b09 flags:# just_clear:flags.0?true peer:InputPeer max_id:int = messages.AffectedHistory;
|
||||
@@ -1001,7 +1021,7 @@ messages.receivedMessages#5a954c0 max_id:int = Vector<ReceivedNotifyMessage>;
|
||||
messages.setTyping#a3825e50 peer:InputPeer action:SendMessageAction = Bool;
|
||||
messages.sendMessage#fa88427a flags:# no_webpage:flags.1?true silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int message:string random_id:long reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.sendMedia#c8f16791 flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int media:InputMedia random_id:long reply_markup:flags.2?ReplyMarkup = Updates;
|
||||
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
|
||||
messages.forwardMessages#708e0195 flags:# silent:flags.5?true background:flags.6?true with_my_score:flags.8?true grouped:flags.9?true from_peer:InputPeer id:Vector<int> random_id:Vector<long> to_peer:InputPeer = Updates;
|
||||
messages.reportSpam#cf1592db peer:InputPeer = Bool;
|
||||
messages.hideReportSpam#a8f1709b peer:InputPeer = Bool;
|
||||
messages.getPeerSettings#3672e09c peer:InputPeer = PeerSettings;
|
||||
@@ -1048,8 +1068,8 @@ messages.getInlineBotResults#514e999d flags:# bot:InputUser peer:InputPeer geo_p
|
||||
messages.setInlineBotResults#eb5ea206 flags:# gallery:flags.0?true private:flags.1?true query_id:long results:Vector<InputBotInlineResult> cache_time:int next_offset:flags.2?string switch_pm:flags.3?InlineBotSwitchPM = Bool;
|
||||
messages.sendInlineBotResult#b16e06fe flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int random_id:long query_id:long id:string = Updates;
|
||||
messages.getMessageEditData#fda68d36 peer:InputPeer id:int = messages.MessageEditData;
|
||||
messages.editMessage#ce91e4ca flags:# no_webpage:flags.1?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Updates;
|
||||
messages.editInlineBotMessage#130c2c85 flags:# no_webpage:flags.1?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> = Bool;
|
||||
messages.editMessage#5d1b8dd flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true peer:InputPeer id:int message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Updates;
|
||||
messages.editInlineBotMessage#b0e08243 flags:# no_webpage:flags.1?true stop_geo_live:flags.12?true id:InputBotInlineMessageID message:flags.11?string reply_markup:flags.2?ReplyMarkup entities:flags.3?Vector<MessageEntity> geo_point:flags.13?InputGeoPoint = Bool;
|
||||
messages.getBotCallbackAnswer#810a9fec flags:# game:flags.1?true peer:InputPeer msg_id:int data:flags.0?bytes = messages.BotCallbackAnswer;
|
||||
messages.setBotCallbackAnswer#d58f130a flags:# alert:flags.1?true query_id:long message:flags.0?string url:flags.2?string cache_time:int = Bool;
|
||||
messages.getPeerDialogs#2d9776b9 peers:Vector<InputPeer> = messages.PeerDialogs;
|
||||
@@ -1080,6 +1100,10 @@ messages.sendScreenshotNotification#c97df020 peer:InputPeer reply_to_msg_id:int
|
||||
messages.getFavedStickers#21ce0b0e hash:int = messages.FavedStickers;
|
||||
messages.faveSticker#b9ffc55b id:InputDocument unfave:Bool = Bool;
|
||||
messages.getUnreadMentions#46578472 peer:InputPeer offset_id:int add_offset:int limit:int max_id:int min_id:int = messages.Messages;
|
||||
messages.readMentions#f0189d3 peer:InputPeer = messages.AffectedHistory;
|
||||
messages.getRecentLocations#249431e2 peer:InputPeer limit:int = messages.Messages;
|
||||
messages.sendMultiMedia#2095512f flags:# silent:flags.5?true background:flags.6?true clear_draft:flags.7?true peer:InputPeer reply_to_msg_id:flags.0?int multi_media:Vector<InputSingleMedia> = Updates;
|
||||
messages.uploadEncryptedFile#5057c497 peer:InputEncryptedChat file:InputEncryptedFile = EncryptedFile;
|
||||
|
||||
updates.getState#edd4882a = updates.State;
|
||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||
@@ -1108,13 +1132,14 @@ help.getAppChangelog#9010ef6f prev_app_version:string = Updates;
|
||||
help.getTermsOfService#350170f3 = help.TermsOfService;
|
||||
help.setBotUpdatesStatus#ec22cfcd pending_updates_count:int message:string = Bool;
|
||||
help.getCdnConfig#52029342 = CdnConfig;
|
||||
help.getRecentMeUrls#3dc0f114 referer:string = help.RecentMeUrls;
|
||||
|
||||
channels.readHistory#cc104937 channel:InputChannel max_id:int = Bool;
|
||||
channels.deleteMessages#84c1fd4e channel:InputChannel id:Vector<int> = messages.AffectedMessages;
|
||||
channels.deleteUserHistory#d10dd71b channel:InputChannel user_id:InputUser = messages.AffectedHistory;
|
||||
channels.reportSpam#fe087810 channel:InputChannel user_id:InputUser id:Vector<int> = Bool;
|
||||
channels.getMessages#93d7b347 channel:InputChannel id:Vector<int> = messages.Messages;
|
||||
channels.getParticipants#24d98f92 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int = channels.ChannelParticipants;
|
||||
channels.getParticipants#123e05e9 channel:InputChannel filter:ChannelParticipantsFilter offset:int limit:int hash:int = channels.ChannelParticipants;
|
||||
channels.getParticipant#546dd7a6 channel:InputChannel user_id:InputUser = channels.ChannelParticipant;
|
||||
channels.getChannels#a7f6bbb id:Vector<InputChannel> = messages.Chats;
|
||||
channels.getFullChannel#8736a09 channel:InputChannel = messages.ChatFull;
|
||||
@@ -1139,6 +1164,8 @@ channels.editBanned#bfd915cd channel:InputChannel user_id:InputUser banned_right
|
||||
channels.getAdminLog#33ddf480 flags:# channel:InputChannel q:string events_filter:flags.0?ChannelAdminLogEventsFilter admins:flags.1?Vector<InputUser> max_id:long min_id:long limit:int = channels.AdminLogResults;
|
||||
channels.setStickers#ea8ca4f9 channel:InputChannel stickerset:InputStickerSet = Bool;
|
||||
channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool;
|
||||
channels.deleteHistory#af369d42 channel:InputChannel max_id:int = Bool;
|
||||
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
||||
|
||||
bots.sendCustomRequest#aa2769ed custom_method:string params:DataJSON = DataJSON;
|
||||
bots.answerWebhookJSONQuery#e6213f4d query_id:long data:DataJSON = Bool;
|
||||
@@ -1169,4 +1196,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
|
||||
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
||||
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
||||
|
||||
// LAYER 71
|
||||
// LAYER 73
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<Identity Name="TelegramMessengerLLP.TelegramDesktop"
|
||||
ProcessorArchitecture="ARCHITECTURE"
|
||||
Publisher="CN=536BC709-8EE1-4478-AF22-F0F0F26FF64A"
|
||||
Version="1.1.23.0" />
|
||||
Version="1.1.29.0" />
|
||||
<Properties>
|
||||
<DisplayName>Telegram Desktop</DisplayName>
|
||||
<PublisherDisplayName>Telegram Messenger LLP</PublisherDisplayName>
|
||||
|
||||
@@ -34,8 +34,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico"
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,1,23,0
|
||||
PRODUCTVERSION 1,1,23,0
|
||||
FILEVERSION 1,1,29,0
|
||||
PRODUCTVERSION 1,1,29,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -52,10 +52,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop"
|
||||
VALUE "FileVersion", "1.1.23.0"
|
||||
VALUE "FileVersion", "1.1.29.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.1.23.0"
|
||||
VALUE "ProductVersion", "1.1.29.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -25,8 +25,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,1,23,0
|
||||
PRODUCTVERSION 1,1,23,0
|
||||
FILEVERSION 1,1,29,0
|
||||
PRODUCTVERSION 1,1,29,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -43,10 +43,10 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Telegram Messenger LLP"
|
||||
VALUE "FileDescription", "Telegram Desktop Updater"
|
||||
VALUE "FileVersion", "1.1.23.0"
|
||||
VALUE "FileVersion", "1.1.29.0"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2014-2017"
|
||||
VALUE "ProductName", "Telegram Desktop"
|
||||
VALUE "ProductVersion", "1.1.23.0"
|
||||
VALUE "ProductVersion", "1.1.29.0"
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
|
||||
@@ -30,18 +30,26 @@ bool equal(const wstring &a, const wstring &b) {
|
||||
|
||||
void updateError(const WCHAR *msg, DWORD errorCode) {
|
||||
WCHAR errMsg[2048];
|
||||
LPWSTR errorText = NULL, errorTextDefault = L"(Unknown error)";
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&errorText, 0, 0);
|
||||
if (!errorText) {
|
||||
errorText = errorTextDefault;
|
||||
}
|
||||
LPWSTR errorTextFormatted = nullptr;
|
||||
auto formatFlags = FORMAT_MESSAGE_FROM_SYSTEM
|
||||
| FORMAT_MESSAGE_ALLOCATE_BUFFER
|
||||
| FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||
FormatMessage(
|
||||
formatFlags,
|
||||
NULL,
|
||||
errorCode,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(LPWSTR)&errorTextFormatted,
|
||||
0,
|
||||
0);
|
||||
auto errorText = errorTextFormatted
|
||||
? errorTextFormatted
|
||||
: L"(Unknown error)";
|
||||
wsprintf(errMsg, L"%s, error code: %d\nError message: %s", msg, errorCode, errorText);
|
||||
|
||||
MessageBox(0, errMsg, L"Update error!", MB_ICONERROR);
|
||||
|
||||
if (errorText != errorTextDefault) {
|
||||
LocalFree(errorText);
|
||||
}
|
||||
LocalFree(errorTextFormatted);
|
||||
}
|
||||
|
||||
HANDLE _logFile = 0;
|
||||
@@ -309,20 +317,20 @@ void updateRegistry() {
|
||||
WCHAR nameStr[bufSize], dateStr[bufSize], publisherStr[bufSize], icongroupStr[bufSize];
|
||||
SYSTEMTIME stLocalTime;
|
||||
GetLocalTime(&stLocalTime);
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"DisplayVersion", 0, REG_SZ, (const BYTE*)versionStr, ((versionLen / 2) + 1) * sizeof(WCHAR));
|
||||
wsprintf(nameStr, L"Telegram Desktop version %s", versionStr);
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"DisplayName", 0, REG_SZ, (const BYTE*)nameStr, (wcslen(nameStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(publisherStr, L"Telegram Messenger LLP");
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"Publisher", 0, REG_SZ, (const BYTE*)publisherStr, (wcslen(publisherStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(icongroupStr, L"Telegram Desktop");
|
||||
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"Inno Setup: Icon Group", 0, REG_SZ, (const BYTE*)icongroupStr, (wcslen(icongroupStr) + 1) * sizeof(WCHAR));
|
||||
wsprintf(dateStr, L"%04d%02d%02d", stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay);
|
||||
RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"InstallDate", 0, REG_SZ, (const BYTE*)dateStr, (wcslen(dateStr) + 1) * sizeof(WCHAR));
|
||||
|
||||
WCHAR *appURL = L"https://desktop.telegram.org";
|
||||
RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
const WCHAR *appURL = L"https://desktop.telegram.org";
|
||||
RegSetValueEx(rkey, L"HelpLink", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLInfoAbout", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
RegSetValueEx(rkey, L"URLUpdateInfo", 0, REG_SZ, (const BYTE*)appURL, (wcslen(appURL) + 1) * sizeof(WCHAR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,14 +20,22 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include "base/timer.h"
|
||||
#include "core/single_timer.h"
|
||||
#include "mtproto/sender.h"
|
||||
#include "base/flat_map.h"
|
||||
#include "base/flat_set.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
|
||||
class AuthSession;
|
||||
|
||||
namespace Storage {
|
||||
enum class SharedMediaType : char;
|
||||
} // namespace Storage
|
||||
|
||||
enum class SparseIdsLoadDirection;
|
||||
|
||||
namespace Api {
|
||||
|
||||
inline const MTPVector<MTPChat> *getChatsFromMessagesChats(const MTPmessages_Chats &chats) {
|
||||
@@ -53,10 +61,14 @@ public:
|
||||
void requestFullPeer(PeerData *peer);
|
||||
void requestPeer(PeerData *peer);
|
||||
void requestPeers(const QList<PeerData*> &peers);
|
||||
void requestLastParticipants(ChannelData *channel, bool fromStart = true);
|
||||
void requestBots(ChannelData *channel);
|
||||
void requestParticipantsCountDelayed(ChannelData *channel);
|
||||
void requestLastParticipants(not_null<ChannelData*> channel);
|
||||
void requestBots(not_null<ChannelData*> channel);
|
||||
void requestAdmins(not_null<ChannelData*> channel);
|
||||
void requestParticipantsCountDelayed(not_null<ChannelData*> channel);
|
||||
|
||||
void requestChannelMembersForAdd(
|
||||
not_null<ChannelData*> channel,
|
||||
base::lambda<void(const MTPchannels_ChannelParticipants&)> callback);
|
||||
void processFullPeer(PeerData *peer, const MTPmessages_ChatFull &result);
|
||||
void processFullPeer(UserData *user, const MTPUserFull &result);
|
||||
|
||||
@@ -89,6 +101,8 @@ public:
|
||||
void handlePrivacyChange(mtpTypeId keyTypeId, const MTPVector<MTPPrivacyRule> &rules);
|
||||
int onlineTillFromStatus(const MTPUserStatus &status, int currentOnlineTill);
|
||||
|
||||
void clearHistory(not_null<PeerData*> peer);
|
||||
|
||||
base::Observable<PeerData*> &fullPeerUpdated() {
|
||||
return _fullPeerUpdated;
|
||||
}
|
||||
@@ -108,6 +122,70 @@ public:
|
||||
bool adminsEnabled,
|
||||
base::flat_set<not_null<UserData*>> &&admins);
|
||||
|
||||
using SliceType = SparseIdsLoadDirection;
|
||||
void requestSharedMedia(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type,
|
||||
MsgId messageId,
|
||||
SliceType slice);
|
||||
void requestSharedMediaCount(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type);
|
||||
|
||||
void requestUserPhotos(
|
||||
not_null<UserData*> user,
|
||||
PhotoId afterId);
|
||||
|
||||
void stickerSetInstalled(uint64 setId) {
|
||||
_stickerSetInstalled.fire_copy(setId);
|
||||
}
|
||||
auto stickerSetInstalled() const {
|
||||
return _stickerSetInstalled.events();
|
||||
}
|
||||
void readFeaturedSetDelayed(uint64 setId);
|
||||
|
||||
void parseChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified = nullptr);
|
||||
void parseRecentChannelParticipants(
|
||||
not_null<ChannelData*> channel,
|
||||
const MTPchannels_ChannelParticipants &result,
|
||||
base::lambda<void(
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list)> callbackList,
|
||||
base::lambda<void()> callbackNotModified = nullptr);
|
||||
|
||||
struct SendOptions {
|
||||
SendOptions(not_null<History*> history) : history(history) {
|
||||
}
|
||||
|
||||
not_null<History*> history;
|
||||
MsgId replyTo = 0;
|
||||
WebPageId webPageId = 0;
|
||||
bool clearDraft = false;
|
||||
bool generateLocal = true;
|
||||
};
|
||||
rpl::producer<SendOptions> sendActions() const {
|
||||
return _sendActions.events();
|
||||
}
|
||||
void sendAction(const SendOptions &options);
|
||||
void forwardMessages(
|
||||
HistoryItemsList &&items,
|
||||
const SendOptions &options,
|
||||
base::lambda_once<void()> &&successCallback = nullptr);
|
||||
void shareContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
const SendOptions &options);
|
||||
void shareContact(not_null<UserData*> user, const SendOptions &options);
|
||||
void readServerHistory(not_null<History*> history);
|
||||
void readServerHistoryForce(not_null<History*> history);
|
||||
|
||||
~ApiWrap();
|
||||
|
||||
private:
|
||||
@@ -117,6 +195,7 @@ private:
|
||||
Callbacks callbacks;
|
||||
};
|
||||
using MessageDataRequests = QMap<MsgId, MessageDataRequest>;
|
||||
using SharedMediaType = Storage::SharedMediaType;
|
||||
|
||||
void requestAppChangelogs();
|
||||
void addLocalChangelogs(int oldAppVersion);
|
||||
@@ -127,13 +206,27 @@ private:
|
||||
|
||||
void resolveMessageDatas();
|
||||
void gotMessageDatas(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId requestId);
|
||||
void finalizeMessageDataRequest(
|
||||
ChannelData *channel,
|
||||
mtpRequestId requestId);
|
||||
|
||||
QVector<MTPint> collectMessageIds(const MessageDataRequests &requests);
|
||||
MessageDataRequests *messageDataRequests(ChannelData *channel, bool onlyExisting = false);
|
||||
|
||||
void gotChatFull(PeerData *peer, const MTPmessages_ChatFull &result, mtpRequestId req);
|
||||
void gotUserFull(UserData *user, const MTPUserFull &result, mtpRequestId req);
|
||||
void lastParticipantsDone(ChannelData *peer, const MTPchannels_ChannelParticipants &result, mtpRequestId req);
|
||||
void applyLastParticipantsList(
|
||||
not_null<ChannelData*> channel,
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list);
|
||||
void applyBotsList(
|
||||
not_null<ChannelData*> channel,
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list);
|
||||
void applyAdminsList(
|
||||
not_null<ChannelData*> channel,
|
||||
int availableCount,
|
||||
const QVector<MTPChannelParticipant> &list);
|
||||
void resolveWebPages();
|
||||
void gotWebPages(ChannelData *channel, const MTPmessages_Messages &result, mtpRequestId req);
|
||||
void gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result);
|
||||
@@ -148,10 +241,48 @@ private:
|
||||
void requestFavedStickers(TimeId now);
|
||||
void requestFeaturedStickers(TimeId now);
|
||||
void requestSavedGifs(TimeId now);
|
||||
void readFeaturedSets();
|
||||
|
||||
void cancelEditChatAdmins(not_null<ChatData*> chat);
|
||||
void saveChatAdmins(not_null<ChatData*> chat);
|
||||
void sendSaveChatAdminsRequests(not_null<ChatData*> chat);
|
||||
void refreshChannelAdmins(
|
||||
not_null<ChannelData*> channel,
|
||||
const QVector<MTPChannelParticipant> &participants);
|
||||
|
||||
template <typename Callback>
|
||||
void requestMessageAfterDate(
|
||||
not_null<PeerData*> peer,
|
||||
const QDate &date,
|
||||
Callback &&callback);
|
||||
|
||||
void sharedMediaDone(
|
||||
not_null<PeerData*> peer,
|
||||
SharedMediaType type,
|
||||
MsgId messageId,
|
||||
SliceType slice,
|
||||
const MTPmessages_Messages &result);
|
||||
|
||||
void userPhotosDone(
|
||||
not_null<UserData*> user,
|
||||
PhotoId photoId,
|
||||
const MTPphotos_Photos &result);
|
||||
|
||||
void sendSharedContact(
|
||||
const QString &phone,
|
||||
const QString &firstName,
|
||||
const QString &lastName,
|
||||
UserId userId,
|
||||
const SendOptions &options);
|
||||
|
||||
void sendReadRequest(not_null<PeerData*> peer, MsgId upTo);
|
||||
int applyAffectedHistory(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_AffectedHistory &result);
|
||||
void applyAffectedMessages(const MTPmessages_AffectedMessages &result);
|
||||
void applyAffectedMessages(
|
||||
not_null<PeerData*> peer,
|
||||
const MTPmessages_AffectedMessages &result);
|
||||
|
||||
not_null<AuthSession*> _session;
|
||||
mtpRequestId _changelogSubscription = 0;
|
||||
@@ -166,8 +297,13 @@ private:
|
||||
|
||||
PeerRequests _participantsRequests;
|
||||
PeerRequests _botsRequests;
|
||||
PeerRequests _adminsRequests;
|
||||
base::DelayedCallTimer _participantsCountRequestTimer;
|
||||
|
||||
ChannelData *_channelMembersForAdd = nullptr;
|
||||
mtpRequestId _channelMembersForAddRequestId = 0;
|
||||
base::lambda<void(const MTPchannels_ChannelParticipants&)> _channelMembersForAddCallback;
|
||||
|
||||
typedef QPair<PeerData*, UserData*> KickRequest;
|
||||
typedef QMap<KickRequest, mtpRequestId> KickRequests;
|
||||
KickRequests _kickRequests;
|
||||
@@ -188,7 +324,7 @@ private:
|
||||
QMap<History*, mtpRequestId> _draftsSaveRequestIds;
|
||||
base::Timer _draftsSaveTimer;
|
||||
|
||||
OrderedSet<mtpRequestId> _stickerSetDisenableRequests;
|
||||
base::flat_set<mtpRequestId> _stickerSetDisenableRequests;
|
||||
Stickers::Order _stickersOrder;
|
||||
mtpRequestId _stickersReorderRequestId = 0;
|
||||
mtpRequestId _stickersClearRecentRequestId = 0;
|
||||
@@ -199,16 +335,49 @@ private:
|
||||
mtpRequestId _featuredStickersUpdateRequest = 0;
|
||||
mtpRequestId _savedGifsUpdateRequest = 0;
|
||||
|
||||
base::Timer _featuredSetsReadTimer;
|
||||
base::flat_set<uint64> _featuredSetsRead;
|
||||
|
||||
QMap<mtpTypeId, mtpRequestId> _privacySaveRequests;
|
||||
|
||||
mtpRequestId _contactsStatusesRequestId = 0;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
|
||||
|
||||
base::flat_map<not_null<ChatData*>, mtpRequestId> _chatAdminsEnabledRequests;
|
||||
base::flat_map<not_null<ChatData*>, base::flat_set<not_null<UserData*>>> _chatAdminsToSave;
|
||||
base::flat_map<not_null<ChatData*>, base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
|
||||
base::flat_map<
|
||||
not_null<ChatData*>,
|
||||
mtpRequestId> _chatAdminsEnabledRequests;
|
||||
base::flat_map<
|
||||
not_null<ChatData*>,
|
||||
base::flat_set<not_null<UserData*>>> _chatAdminsToSave;
|
||||
base::flat_map<
|
||||
not_null<ChatData*>,
|
||||
base::flat_set<mtpRequestId>> _chatAdminsSaveRequests;
|
||||
|
||||
base::flat_map<std::tuple<
|
||||
not_null<PeerData*>,
|
||||
SharedMediaType,
|
||||
MsgId,
|
||||
SliceType>, mtpRequestId> _sharedMediaRequests;
|
||||
|
||||
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
|
||||
|
||||
rpl::event_stream<SendOptions> _sendActions;
|
||||
|
||||
struct ReadRequest {
|
||||
ReadRequest(mtpRequestId requestId, MsgId upTo)
|
||||
: requestId(requestId)
|
||||
, upTo(upTo) {
|
||||
}
|
||||
|
||||
mtpRequestId requestId = 0;
|
||||
MsgId upTo = 0;
|
||||
};
|
||||
base::flat_map<not_null<PeerData*>, ReadRequest> _readRequests;
|
||||
base::flat_map<not_null<PeerData*>, MsgId> _readRequestsPending;
|
||||
|
||||
base::Observable<PeerData*> _fullPeerUpdated;
|
||||
|
||||
rpl::event_stream<uint64> _stickerSetInstalled;
|
||||
|
||||
};
|
||||
|
||||
@@ -46,6 +46,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "numbers.h"
|
||||
#include "observer_peer.h"
|
||||
#include "auth_session.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/storage_shared_media.h"
|
||||
#include "window/themes/window_theme.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
@@ -58,7 +60,7 @@ namespace {
|
||||
using PeersData = QHash<PeerId, PeerData*>;
|
||||
PeersData peersData;
|
||||
|
||||
using MutedPeers = QMap<PeerData*, bool>;
|
||||
using MutedPeers = QMap<not_null<PeerData*>, bool>;
|
||||
MutedPeers mutedPeers;
|
||||
|
||||
PhotosData photosData;
|
||||
@@ -193,9 +195,6 @@ namespace {
|
||||
Window::Theme::Background()->reset();
|
||||
|
||||
cSetOtherOnline(0);
|
||||
globalNotifyAllPtr = UnknownNotifySettings;
|
||||
globalNotifyUsersPtr = UnknownNotifySettings;
|
||||
globalNotifyChatsPtr = UnknownNotifySettings;
|
||||
clearStorageImages();
|
||||
if (auto w = wnd()) {
|
||||
w->updateConnectingStatus();
|
||||
@@ -206,7 +205,11 @@ namespace {
|
||||
|
||||
void logOut() {
|
||||
if (auto mtproto = Messenger::Instance().mtp()) {
|
||||
mtproto->logout(rpcDone(&loggedOut), rpcFail(&loggedOut));
|
||||
mtproto->logout(rpcDone([] {
|
||||
return loggedOut();
|
||||
}), rpcFail([] {
|
||||
return loggedOut();
|
||||
}));
|
||||
} else {
|
||||
// We log out because we've forgotten passcode.
|
||||
// So we just start mtproto from scratch.
|
||||
@@ -254,11 +257,11 @@ namespace {
|
||||
|
||||
int32 onlineWillChangeIn(TimeId online, TimeId now) {
|
||||
if (online <= 0) {
|
||||
if (-online > now) return -online - now;
|
||||
if (-online > now) return std::max(-online - now, 86400);
|
||||
return 86400;
|
||||
}
|
||||
if (online > now) {
|
||||
return online - now;
|
||||
return std::max(online - now, 86400);
|
||||
}
|
||||
int32 minutes = (now - online) / 60;
|
||||
if (minutes < 60) {
|
||||
@@ -269,7 +272,7 @@ namespace {
|
||||
return (hours + 1) * 3600 - (now - online);
|
||||
}
|
||||
QDateTime dNow(date(now)), dTomorrow(dNow.date().addDays(1));
|
||||
return dNow.secsTo(dTomorrow);
|
||||
return std::max(dNow.secsTo(dTomorrow), 86400LL);
|
||||
}
|
||||
|
||||
QString onlineText(UserData *user, TimeId now, bool precise) {
|
||||
@@ -286,8 +289,8 @@ namespace {
|
||||
QString onlineText(TimeId online, TimeId now, bool precise) {
|
||||
if (online <= 0) {
|
||||
switch (online) {
|
||||
case 0: return lang(lng_status_offline);
|
||||
case -1: return lang(lng_status_invisible);
|
||||
case 0:
|
||||
case -1: return lang(lng_status_offline);
|
||||
case -2: return lang(lng_status_recently);
|
||||
case -3: return lang(lng_status_last_week);
|
||||
case -4: return lang(lng_status_last_month);
|
||||
@@ -392,8 +395,8 @@ namespace {
|
||||
data->inputUser = MTP_inputUser(d.vid, MTP_long(0));
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->setIsInaccessible();
|
||||
data->flags = 0;
|
||||
//data->setFlags(MTPDuser_ClientFlag::f_inaccessible | 0);
|
||||
data->setFlags(MTPDuser::Flag::f_deleted);
|
||||
data->setBotInfoVersion(-1);
|
||||
status = &emptyStatus;
|
||||
data->contact = -1;
|
||||
@@ -409,14 +412,19 @@ namespace {
|
||||
data = App::user(peer);
|
||||
auto canShareThisContact = data->canShareThisContactFast();
|
||||
wasContact = data->isContact();
|
||||
if (!minimal) {
|
||||
data->flags = d.vflags.v;
|
||||
if (minimal) {
|
||||
auto mask = 0
|
||||
//| MTPDuser_ClientFlag::f_inaccessible
|
||||
| MTPDuser::Flag::f_deleted;
|
||||
data->setFlags((data->flags() & ~mask) | (d.vflags.v & mask));
|
||||
} else {
|
||||
data->setFlags(d.vflags.v);
|
||||
if (d.is_self()) {
|
||||
data->input = MTP_inputPeerSelf();
|
||||
data->inputUser = MTP_inputUserSelf();
|
||||
} else if (!d.has_access_hash()) {
|
||||
data->input = MTP_inputPeerUser(d.vid, MTP_long(data->isInaccessible() ? 0 : data->access));
|
||||
data->inputUser = MTP_inputUser(d.vid, MTP_long(data->isInaccessible() ? 0 : data->access));
|
||||
data->input = MTP_inputPeerUser(d.vid, MTP_long(data->accessHash()));
|
||||
data->inputUser = MTP_inputUser(d.vid, MTP_long(data->accessHash()));
|
||||
} else {
|
||||
data->input = MTP_inputPeerUser(d.vid, d.vaccess_hash);
|
||||
data->inputUser = MTP_inputUser(d.vid, d.vaccess_hash);
|
||||
@@ -434,7 +442,6 @@ namespace {
|
||||
}
|
||||
data->setName(lang(lng_deleted), QString(), QString(), QString());
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
data->setIsInaccessible();
|
||||
status = &emptyStatus;
|
||||
} else {
|
||||
// apply first_name and last_name from minimal user only if we don't have
|
||||
@@ -473,7 +480,9 @@ namespace {
|
||||
} else {
|
||||
data->setPhoto(MTP_userProfilePhotoEmpty());
|
||||
}
|
||||
if (d.has_access_hash()) data->access = d.vaccess_hash.v;
|
||||
if (d.has_access_hash()) {
|
||||
data->setAccessHash(d.vaccess_hash.v);
|
||||
}
|
||||
status = d.has_status() ? &d.vstatus : &emptyStatus;
|
||||
}
|
||||
if (!minimal) {
|
||||
@@ -577,12 +586,9 @@ namespace {
|
||||
cdata->date = d.vdate.v;
|
||||
|
||||
if (d.has_migrated_to() && d.vmigrated_to.type() == mtpc_inputChannel) {
|
||||
const auto &c(d.vmigrated_to.c_inputChannel());
|
||||
ChannelData *channel = App::channel(peerFromChannel(c.vchannel_id));
|
||||
if (!channel->mgInfo) {
|
||||
channel->flags |= MTPDchannel::Flag::f_megagroup;
|
||||
channel->flagsUpdated();
|
||||
}
|
||||
auto &c = d.vmigrated_to.c_inputChannel();
|
||||
auto channel = App::channel(peerFromChannel(c.vchannel_id));
|
||||
channel->addFlags(MTPDchannel::Flag::f_megagroup);
|
||||
if (!channel->access) {
|
||||
channel->input = MTP_inputPeerChannel(c.vchannel_id, c.vaccess_hash);
|
||||
channel->inputChannel = d.vmigrated_to;
|
||||
@@ -594,8 +600,8 @@ namespace {
|
||||
}
|
||||
if (updatedFrom) {
|
||||
channel->mgInfo->migrateFromPtr = cdata;
|
||||
if (History *h = App::historyLoaded(cdata->id)) {
|
||||
if (History *hto = App::historyLoaded(channel->id)) {
|
||||
if (auto h = App::historyLoaded(cdata->id)) {
|
||||
if (auto hto = App::historyLoaded(channel->id)) {
|
||||
if (!h->isEmpty()) {
|
||||
h->clear(true);
|
||||
}
|
||||
@@ -613,13 +619,12 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
if (!(cdata->flags & MTPDchat::Flag::f_admins_enabled) && (d.vflags.v & MTPDchat::Flag::f_admins_enabled)) {
|
||||
if (!(cdata->flags() & MTPDchat::Flag::f_admins_enabled) && (d.vflags.v & MTPDchat::Flag::f_admins_enabled)) {
|
||||
cdata->invalidateParticipants();
|
||||
}
|
||||
cdata->flags = d.vflags.v;
|
||||
cdata->setFlags(d.vflags.v);
|
||||
|
||||
cdata->count = d.vparticipants_count.v;
|
||||
cdata->setIsForbidden(false);
|
||||
if (canEdit != cdata->canEdit()) {
|
||||
update.flags |= UpdateFlag::ChatCanEdit;
|
||||
}
|
||||
@@ -637,8 +642,7 @@ namespace {
|
||||
cdata->date = 0;
|
||||
cdata->count = -1;
|
||||
cdata->invalidateParticipants();
|
||||
cdata->flags = 0;
|
||||
cdata->setIsForbidden(true);
|
||||
cdata->setFlags(MTPDchat_ClientFlag::f_forbidden | 0);
|
||||
if (canEdit != cdata->canEdit()) {
|
||||
update.flags |= UpdateFlag::ChatCanEdit;
|
||||
}
|
||||
@@ -665,8 +669,13 @@ namespace {
|
||||
auto canAddMembers = cdata->canAddMembers();
|
||||
|
||||
if (minimal) {
|
||||
auto mask = MTPDchannel::Flag::f_broadcast | MTPDchannel::Flag::f_verified | MTPDchannel::Flag::f_megagroup | MTPDchannel::Flag::f_democracy;
|
||||
cdata->flags = (cdata->flags & ~mask) | (d.vflags.v & mask);
|
||||
auto mask = 0
|
||||
| MTPDchannel::Flag::f_broadcast
|
||||
| MTPDchannel::Flag::f_verified
|
||||
| MTPDchannel::Flag::f_megagroup
|
||||
| MTPDchannel::Flag::f_democracy
|
||||
| MTPDchannel_ClientFlag::f_forbidden;
|
||||
cdata->setFlags((cdata->flags() & ~mask) | (d.vflags.v & mask));
|
||||
} else {
|
||||
if (d.has_admin_rights()) {
|
||||
cdata->setAdminRights(d.vadmin_rights);
|
||||
@@ -675,7 +684,7 @@ namespace {
|
||||
}
|
||||
if (d.has_banned_rights()) {
|
||||
cdata->setRestrictedRights(d.vbanned_rights);
|
||||
} else if (cdata->hasRestrictedRights()) {
|
||||
} else if (cdata->hasRestrictions()) {
|
||||
cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
|
||||
}
|
||||
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
||||
@@ -689,14 +698,12 @@ namespace {
|
||||
} else {
|
||||
cdata->setRestrictionReason(QString());
|
||||
}
|
||||
cdata->flags = d.vflags.v;
|
||||
cdata->setFlags(d.vflags.v);
|
||||
}
|
||||
cdata->flagsUpdated();
|
||||
|
||||
QString uname = d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString();
|
||||
cdata->setName(qs(d.vtitle), uname);
|
||||
|
||||
cdata->setIsForbidden(false);
|
||||
cdata->setPhoto(d.vphoto);
|
||||
|
||||
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
|
||||
@@ -720,13 +727,12 @@ namespace {
|
||||
cdata->inputChannel = MTP_inputChannel(d.vid, d.vaccess_hash);
|
||||
|
||||
auto mask = mtpCastFlags(MTPDchannelForbidden::Flag::f_broadcast | MTPDchannelForbidden::Flag::f_megagroup);
|
||||
cdata->flags = (cdata->flags & ~mask) | (mtpCastFlags(d.vflags) & mask);
|
||||
cdata->flagsUpdated();
|
||||
cdata->setFlags((cdata->flags() & ~mask) | (mtpCastFlags(d.vflags) & mask) | MTPDchannel_ClientFlag::f_forbidden);
|
||||
|
||||
if (cdata->hasAdminRights()) {
|
||||
cdata->setAdminRights(MTP_channelAdminRights(MTP_flags(0)));
|
||||
}
|
||||
if (cdata->hasRestrictedRights()) {
|
||||
if (cdata->hasRestrictions()) {
|
||||
cdata->setRestrictedRights(MTP_channelBannedRights(MTP_flags(0), MTP_int(0)));
|
||||
}
|
||||
|
||||
@@ -736,7 +742,6 @@ namespace {
|
||||
cdata->setPhoto(MTP_chatPhotoEmpty());
|
||||
cdata->date = 0;
|
||||
cdata->setMembersCount(0);
|
||||
cdata->setIsForbidden(true);
|
||||
|
||||
if (wasInChannel != cdata->amIn()) update.flags |= UpdateFlag::ChannelAmIn;
|
||||
if (canViewAdmins != cdata->canViewAdmins()
|
||||
@@ -790,10 +795,12 @@ namespace {
|
||||
chat->version = d.vversion.v;
|
||||
auto &v = d.vparticipants.v;
|
||||
chat->count = v.size();
|
||||
int32 pversion = chat->participants.isEmpty() ? 1 : (chat->participants.begin().value() + 1);
|
||||
int32 pversion = chat->participants.empty()
|
||||
? 1
|
||||
: (chat->participants.begin()->second + 1);
|
||||
chat->invitedByMe.clear();
|
||||
chat->admins.clear();
|
||||
chat->flags &= ~MTPDchat::Flag::f_admin;
|
||||
chat->removeFlags(MTPDchat::Flag::f_admin);
|
||||
for (auto i = v.cbegin(), e = v.cend(); i != e; ++i) {
|
||||
int32 uid = 0, inviter = 0;
|
||||
switch (i->type()) {
|
||||
@@ -824,7 +831,7 @@ namespace {
|
||||
if (i->type() == mtpc_chatParticipantAdmin) {
|
||||
chat->admins.insert(user);
|
||||
if (user->isSelf()) {
|
||||
chat->flags |= MTPDchat::Flag::f_admin;
|
||||
chat->addFlags(MTPDchat::Flag::f_admin);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -832,21 +839,22 @@ namespace {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!chat->participants.isEmpty()) {
|
||||
History *h = App::historyLoaded(chat->id);
|
||||
if (!chat->participants.empty()) {
|
||||
auto h = App::historyLoaded(chat->id);
|
||||
bool found = !h || !h->lastKeyboardFrom;
|
||||
int32 botStatus = -1;
|
||||
auto botStatus = -1;
|
||||
for (auto i = chat->participants.begin(), e = chat->participants.end(); i != e;) {
|
||||
if (i.value() < pversion) {
|
||||
auto [user, version] = *i;
|
||||
if (version < pversion) {
|
||||
i = chat->participants.erase(i);
|
||||
} else {
|
||||
if (i.key()->botInfo) {
|
||||
botStatus = 2;// (botStatus > 0/* || !i.key()->botInfo->readsAllHistory*/) ? 2 : 1;
|
||||
if (requestBotInfos && !i.key()->botInfo->inited) {
|
||||
Auth().api().requestFullPeer(i.key());
|
||||
if (user->botInfo) {
|
||||
botStatus = 2;// (botStatus > 0/* || !user->botInfo->readsAllHistory*/) ? 2 : 1;
|
||||
if (requestBotInfos && !user->botInfo->inited) {
|
||||
Auth().api().requestFullPeer(user);
|
||||
}
|
||||
}
|
||||
if (!found && i.key()->id == h->lastKeyboardFrom) {
|
||||
if (!found && user->id == h->lastKeyboardFrom) {
|
||||
found = true;
|
||||
}
|
||||
++i;
|
||||
@@ -876,11 +884,11 @@ namespace {
|
||||
chat->version = d.vversion.v;
|
||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||
if (user) {
|
||||
if (chat->participants.isEmpty() && chat->count) {
|
||||
if (chat->participants.empty() && chat->count) {
|
||||
chat->count++;
|
||||
chat->botStatus = 0;
|
||||
} else if (chat->participants.find(user) == chat->participants.end()) {
|
||||
chat->participants[user] = (chat->participants.isEmpty() ? 1 : chat->participants.begin().value());
|
||||
chat->participants[user] = (chat->participants.empty() ? 1 : chat->participants.begin()->second);
|
||||
if (d.vinviter_id.v == Auth().userId()) {
|
||||
chat->invitedByMe.insert(user);
|
||||
} else {
|
||||
@@ -913,7 +921,7 @@ namespace {
|
||||
auto canEdit = chat->canEdit();
|
||||
UserData *user = App::userLoaded(d.vuser_id.v);
|
||||
if (user) {
|
||||
if (chat->participants.isEmpty()) {
|
||||
if (chat->participants.empty()) {
|
||||
if (chat->count > 0) {
|
||||
chat->count--;
|
||||
}
|
||||
@@ -925,7 +933,7 @@ namespace {
|
||||
chat->invitedByMe.remove(user);
|
||||
chat->admins.remove(user);
|
||||
if (user->isSelf()) {
|
||||
chat->flags &= ~MTPDchat::Flag::f_admin;
|
||||
chat->removeFlags(MTPDchat::Flag::f_admin);
|
||||
}
|
||||
|
||||
History *h = App::historyLoaded(chat->id);
|
||||
@@ -935,9 +943,9 @@ namespace {
|
||||
}
|
||||
if (chat->botStatus > 0 && user->botInfo) {
|
||||
int32 botStatus = -1;
|
||||
for (auto j = chat->participants.cbegin(), e = chat->participants.cend(); j != e; ++j) {
|
||||
if (j.key()->botInfo) {
|
||||
if (true || botStatus > 0/* || !j.key()->botInfo->readsAllHistory*/) {
|
||||
for (auto [participant, v] : chat->participants) {
|
||||
if (participant->botInfo) {
|
||||
if (true || botStatus > 0/* || !participant->botInfo->readsAllHistory*/) {
|
||||
botStatus = 2;
|
||||
break;
|
||||
}
|
||||
@@ -959,23 +967,28 @@ namespace {
|
||||
}
|
||||
|
||||
void feedChatAdmins(const MTPDupdateChatAdmins &d) {
|
||||
ChatData *chat = App::chat(d.vchat_id.v);
|
||||
auto chat = App::chat(d.vchat_id.v);
|
||||
if (chat->version <= d.vversion.v) {
|
||||
bool badVersion = (chat->version + 1 < d.vversion.v);
|
||||
if (badVersion) {
|
||||
auto wasCanEdit = chat->canEdit();
|
||||
auto badVersion = (chat->version + 1 < d.vversion.v);
|
||||
chat->version = d.vversion.v;
|
||||
if (mtpIsTrue(d.venabled)) {
|
||||
chat->addFlags(MTPDchat::Flag::f_admins_enabled);
|
||||
} else {
|
||||
chat->removeFlags(MTPDchat::Flag::f_admins_enabled);
|
||||
}
|
||||
if (badVersion || mtpIsTrue(d.venabled)) {
|
||||
chat->invalidateParticipants();
|
||||
Auth().api().requestPeer(chat);
|
||||
}
|
||||
chat->version = d.vversion.v;
|
||||
if (mtpIsTrue(d.venabled)) {
|
||||
if (!badVersion) {
|
||||
chat->invalidateParticipants();
|
||||
}
|
||||
chat->flags |= MTPDchat::Flag::f_admins_enabled;
|
||||
} else {
|
||||
chat->flags &= ~MTPDchat::Flag::f_admins_enabled;
|
||||
if (wasCanEdit != chat->canEdit()) {
|
||||
Notify::peerUpdatedDelayed(
|
||||
chat,
|
||||
Notify::PeerUpdate::Flag::ChatCanEdit);
|
||||
}
|
||||
Notify::peerUpdatedDelayed(chat, Notify::PeerUpdate::Flag::AdminsChanged);
|
||||
Notify::peerUpdatedDelayed(
|
||||
chat,
|
||||
Notify::PeerUpdate::Flag::AdminsChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -992,7 +1005,7 @@ namespace {
|
||||
if (user) {
|
||||
if (mtpIsTrue(d.vis_admin)) {
|
||||
if (user->isSelf()) {
|
||||
chat->flags |= MTPDchat::Flag::f_admin;
|
||||
chat->addFlags(MTPDchat::Flag::f_admin);
|
||||
}
|
||||
if (chat->noParticipantInfo()) {
|
||||
Auth().api().requestFullPeer(chat);
|
||||
@@ -1001,7 +1014,7 @@ namespace {
|
||||
}
|
||||
} else {
|
||||
if (user->isSelf()) {
|
||||
chat->flags &= ~MTPDchat::Flag::f_admin;
|
||||
chat->removeFlags(MTPDchat::Flag::f_admin);
|
||||
}
|
||||
chat->admins.remove(user);
|
||||
}
|
||||
@@ -1027,7 +1040,13 @@ namespace {
|
||||
existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr);
|
||||
existing->updateReplyMarkup(m.has_reply_markup() ? (&m.vreply_markup) : nullptr);
|
||||
existing->setViewsCount(m.has_views() ? m.vviews.v : -1);
|
||||
existing->addToOverview(AddToOverviewNew);
|
||||
existing->addToUnreadMentions(AddToUnreadMentionsMethod::New);
|
||||
if (auto sharedMediaTypes = existing->sharedMediaTypes()) {
|
||||
Auth().storage().add(Storage::SharedMediaAddNew(
|
||||
peerId,
|
||||
sharedMediaTypes,
|
||||
existing->id));
|
||||
}
|
||||
|
||||
if (!existing->detached()) {
|
||||
App::checkSavedGif(existing);
|
||||
@@ -1039,27 +1058,26 @@ namespace {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename TMTPDclass>
|
||||
void updateEditedMessage(const TMTPDclass &m) {
|
||||
auto peerId = peerFromMTP(m.vto_id);
|
||||
if (m.has_from_id() && peerId == Auth().userPeerId()) {
|
||||
peerId = peerFromUser(m.vfrom_id);
|
||||
}
|
||||
if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) {
|
||||
existing->applyEdition(m);
|
||||
}
|
||||
}
|
||||
|
||||
void updateEditedMessage(const MTPMessage &m) {
|
||||
auto apply = [](const auto &data) {
|
||||
auto peerId = peerFromMTP(data.vto_id);
|
||||
if (data.has_from_id() && peerId == Auth().userPeerId()) {
|
||||
peerId = peerFromUser(data.vfrom_id);
|
||||
}
|
||||
if (auto existing = App::histItemById(peerToChannel(peerId), data.vid.v)) {
|
||||
existing->applyEdition(data);
|
||||
}
|
||||
};
|
||||
|
||||
if (m.type() == mtpc_message) { // apply message edit
|
||||
App::updateEditedMessage(m.c_message());
|
||||
apply(m.c_message());
|
||||
} else if (m.type() == mtpc_messageService) {
|
||||
App::updateEditedMessage(m.c_messageService());
|
||||
apply(m.c_messageService());
|
||||
}
|
||||
}
|
||||
|
||||
void addSavedGif(DocumentData *doc) {
|
||||
SavedGifs &saved(cRefSavedGifs());
|
||||
auto &saved = Auth().data().savedGifsRef();
|
||||
int32 index = saved.indexOf(doc);
|
||||
if (index) {
|
||||
if (index > 0) saved.remove(index);
|
||||
@@ -1067,8 +1085,8 @@ namespace {
|
||||
if (saved.size() > Global::SavedGifsLimit()) saved.pop_back();
|
||||
Local::writeSavedGifs();
|
||||
|
||||
Auth().data().savedGifsUpdated().notify();
|
||||
cSetLastSavedGifsUpdate(0);
|
||||
Auth().data().markSavedGifsUpdated();
|
||||
Auth().data().setLastSavedGifsUpdate(0);
|
||||
Auth().api().updateStickers();
|
||||
}
|
||||
}
|
||||
@@ -1140,28 +1158,6 @@ namespace {
|
||||
return ImagePtr();
|
||||
}
|
||||
|
||||
StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc) {
|
||||
if (loc.type() == mtpc_fileLocation) {
|
||||
const auto &l(loc.c_fileLocation());
|
||||
return StorageImageLocation(w, h, l.vdc_id.v, l.vvolume_id.v, l.vlocal_id.v, l.vsecret.v);
|
||||
}
|
||||
return StorageImageLocation(w, h, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size) {
|
||||
switch (size.type()) {
|
||||
case mtpc_photoSize: {
|
||||
const auto &d(size.c_photoSize());
|
||||
return imageLocation(d.vw.v, d.vh.v, d.vlocation);
|
||||
} break;
|
||||
case mtpc_photoCachedSize: {
|
||||
const auto &d(size.c_photoCachedSize());
|
||||
return imageLocation(d.vw.v, d.vh.v, d.vlocation);
|
||||
} break;
|
||||
}
|
||||
return StorageImageLocation();
|
||||
}
|
||||
|
||||
void feedInboxRead(const PeerId &peer, MsgId upTo) {
|
||||
if (auto history = App::historyLoaded(peer)) {
|
||||
history->inboxRead(upTo);
|
||||
@@ -1399,7 +1395,18 @@ namespace {
|
||||
}
|
||||
|
||||
DocumentData *feedDocument(const MTPDdocument &document, DocumentData *convert) {
|
||||
return App::documentSet(document.vid.v, convert, document.vaccess_hash.v, document.vversion.v, document.vdate.v, document.vattributes.v, qs(document.vmime_type), App::image(document.vthumb), document.vdc_id.v, document.vsize.v, App::imageLocation(document.vthumb));
|
||||
return App::documentSet(
|
||||
document.vid.v,
|
||||
convert,
|
||||
document.vaccess_hash.v,
|
||||
document.vversion.v,
|
||||
document.vdate.v,
|
||||
document.vattributes.v,
|
||||
qs(document.vmime_type),
|
||||
App::image(document.vthumb),
|
||||
document.vdc_id.v,
|
||||
document.vsize.v,
|
||||
StorageImageLocation::FromMTP(document.vthumb));
|
||||
}
|
||||
|
||||
WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) {
|
||||
@@ -1596,7 +1603,7 @@ namespace {
|
||||
versionChanged = convert->setRemoteVersion(version);
|
||||
convert->setRemoteLocation(dc, access);
|
||||
convert->date = date;
|
||||
convert->mime = mime;
|
||||
convert->setMimeString(mime);
|
||||
if (!thumb->isNull() && (convert->thumb->isNull() || convert->thumb->width() < thumb->width() || convert->thumb->height() < thumb->height() || versionChanged)) {
|
||||
updateImage(convert->thumb, thumb);
|
||||
}
|
||||
@@ -1616,7 +1623,7 @@ namespace {
|
||||
}
|
||||
}
|
||||
|
||||
if (cSavedGifs().indexOf(convert) >= 0) { // id changed
|
||||
if (Auth().data().savedGifs().indexOf(convert) >= 0) { // id changed
|
||||
Local::writeSavedGifs();
|
||||
}
|
||||
}
|
||||
@@ -1628,7 +1635,7 @@ namespace {
|
||||
} else {
|
||||
result = DocumentData::create(document, dc, access, version, attributes);
|
||||
result->date = date;
|
||||
result->mime = mime;
|
||||
result->setMimeString(mime);
|
||||
result->thumb = thumb;
|
||||
result->size = size;
|
||||
result->recountIsImage();
|
||||
@@ -1646,7 +1653,7 @@ namespace {
|
||||
result->setRemoteLocation(dc, access);
|
||||
}
|
||||
result->date = date;
|
||||
result->mime = mime;
|
||||
result->setMimeString(mime);
|
||||
if (!thumb->isNull() && (result->thumb->isNull() || result->thumb->width() < thumb->width() || result->thumb->height() < thumb->height() || versionChanged)) {
|
||||
result->thumb = thumb;
|
||||
}
|
||||
@@ -1662,8 +1669,8 @@ namespace {
|
||||
}
|
||||
if (versionChanged) {
|
||||
if (result->sticker() && result->sticker()->set.type() == mtpc_inputStickerSetID) {
|
||||
auto it = Global::StickerSets().constFind(result->sticker()->set.c_inputStickerSetID().vid.v);
|
||||
if (it != Global::StickerSets().cend()) {
|
||||
auto it = Auth().data().stickerSets().constFind(result->sticker()->set.c_inputStickerSetID().vid.v);
|
||||
if (it != Auth().data().stickerSets().cend()) {
|
||||
if (it->id == Stickers::CloudRecentSetId) {
|
||||
Local::writeRecentStickers();
|
||||
} else if (it->id == Stickers::FavedSetId) {
|
||||
@@ -1938,8 +1945,10 @@ namespace {
|
||||
}
|
||||
}
|
||||
Auth().notifications().clearFromItem(item);
|
||||
if (Global::started() && !App::quitting()) {
|
||||
Global::RefItemRemoved().notify(item, true);
|
||||
if (Global::started()
|
||||
&& !App::quitting()
|
||||
&& AuthSession::Exists()) {
|
||||
Auth().data().markItemRemoved(item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2013,19 +2022,6 @@ namespace {
|
||||
Auth().api().clearWebPageRequests();
|
||||
}
|
||||
cSetRecentStickers(RecentStickerPack());
|
||||
Global::SetStickerSets(Stickers::Sets());
|
||||
Global::SetStickerSetsOrder(Stickers::Order());
|
||||
Global::SetLastStickersUpdate(0);
|
||||
Global::SetLastRecentStickersUpdate(0);
|
||||
Global::SetFeaturedStickerSetsOrder(Stickers::Order());
|
||||
if (Global::FeaturedStickerSetsUnreadCount() != 0) {
|
||||
Global::SetFeaturedStickerSetsUnreadCount(0);
|
||||
Global::RefFeaturedStickerSetsUnreadCountChanged().notify();
|
||||
}
|
||||
Global::SetLastFeaturedStickersUpdate(0);
|
||||
Global::SetArchivedStickerSetsOrder(Stickers::Order());
|
||||
cSetSavedGifs(SavedGifs());
|
||||
cSetLastSavedGifsUpdate(0);
|
||||
cSetReportSpamStatuses(ReportSpamStatuses());
|
||||
cSetAutoDownloadPhoto(0);
|
||||
cSetAutoDownloadAudio(0);
|
||||
@@ -2587,28 +2583,32 @@ namespace {
|
||||
return QString();
|
||||
}
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn) {
|
||||
void regMuted(not_null<PeerData*> peer, TimeMs changeIn) {
|
||||
::mutedPeers.insert(peer, true);
|
||||
if (App::main()) App::main()->updateMutedIn(changeIn);
|
||||
App::main()->updateMutedIn(changeIn);
|
||||
}
|
||||
|
||||
void unregMuted(PeerData *peer) {
|
||||
void unregMuted(not_null<PeerData*> peer) {
|
||||
::mutedPeers.remove(peer);
|
||||
}
|
||||
|
||||
void updateMuted() {
|
||||
int32 changeInMin = 0;
|
||||
for (MutedPeers::iterator i = ::mutedPeers.begin(); i != ::mutedPeers.end();) {
|
||||
int32 changeIn = 0;
|
||||
History *h = App::history(i.key()->id);
|
||||
if (isNotifyMuted(i.key()->notify, &changeIn)) {
|
||||
h->setMute(true);
|
||||
if (changeIn && (!changeInMin || changeIn < changeInMin)) {
|
||||
changeInMin = changeIn;
|
||||
auto changeInMin = TimeMs(0);
|
||||
for (auto i = ::mutedPeers.begin(); i != ::mutedPeers.end();) {
|
||||
const auto history = App::historyLoaded(i.key()->id);
|
||||
const auto muteFinishesIn = i.key()->notifyMuteFinishesIn();
|
||||
if (muteFinishesIn > 0) {
|
||||
if (history) {
|
||||
history->changeMute(true);
|
||||
}
|
||||
if (!changeInMin || muteFinishesIn < changeInMin) {
|
||||
changeInMin = muteFinishesIn;
|
||||
}
|
||||
++i;
|
||||
} else {
|
||||
h->setMute(false);
|
||||
if (history) {
|
||||
history->changeMute(false);
|
||||
}
|
||||
i = ::mutedPeers.erase(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,8 +82,6 @@ namespace App {
|
||||
void feedUserLink(MTPint userId, const MTPContactLink &myLink, const MTPContactLink &foreignLink);
|
||||
|
||||
ImagePtr image(const MTPPhotoSize &size);
|
||||
StorageImageLocation imageLocation(int32 w, int32 h, const MTPFileLocation &loc);
|
||||
StorageImageLocation imageLocation(const MTPPhotoSize &size);
|
||||
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, const PreparedPhotoThumbs &thumbs);
|
||||
PhotoData *feedPhoto(const MTPPhoto &photo, PhotoData *convert = nullptr);
|
||||
@@ -262,8 +260,8 @@ namespace App {
|
||||
void stopRoundVideoPlayback();
|
||||
void stopGifItems();
|
||||
|
||||
void regMuted(PeerData *peer, int32 changeIn);
|
||||
void unregMuted(PeerData *peer);
|
||||
void regMuted(not_null<PeerData*> peer, TimeMs changeIn);
|
||||
void unregMuted(not_null<PeerData*> peer);
|
||||
void updateMuted();
|
||||
|
||||
void setProxySettings(QNetworkAccessManager &manager);
|
||||
|
||||
@@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "storage/file_download.h"
|
||||
#include "storage/file_upload.h"
|
||||
#include "storage/localstorage.h"
|
||||
#include "storage/storage_facade.h"
|
||||
#include "storage/serialize_common.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "platform/platform_specific.h"
|
||||
@@ -45,7 +46,7 @@ AuthSessionData::Variables::Variables()
|
||||
}
|
||||
|
||||
QByteArray AuthSessionData::serialize() const {
|
||||
auto size = sizeof(qint32) * 8;
|
||||
auto size = sizeof(qint32) * 10;
|
||||
for (auto i = _variables.soundOverrides.cbegin(), e = _variables.soundOverrides.cend(); i != e; ++i) {
|
||||
size += Serialize::stringSize(i.key()) + Serialize::stringSize(i.value());
|
||||
}
|
||||
@@ -70,6 +71,14 @@ QByteArray AuthSessionData::serialize() const {
|
||||
for (auto peerId : _variables.groupStickersSectionHidden) {
|
||||
stream << quint64(peerId);
|
||||
}
|
||||
stream << qint32(_variables.thirdSectionInfoEnabled ? 1 : 0);
|
||||
stream << qint32(_variables.smallDialogsList ? 1 : 0);
|
||||
stream << qint32(snap(
|
||||
qRound(_variables.dialogsWidthRatio.current() * 1000000),
|
||||
0,
|
||||
1000000));
|
||||
stream << qint32(_variables.thirdColumnWidth.current());
|
||||
stream << qint32(_variables.thirdSectionExtendedBy);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -88,7 +97,12 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
|
||||
qint32 floatPlayerColumn = static_cast<qint32>(Window::Column::Second);
|
||||
qint32 floatPlayerCorner = static_cast<qint32>(RectPart::TopRight);
|
||||
QMap<QString, QString> soundOverrides;
|
||||
OrderedSet<PeerId> groupStickersSectionHidden;
|
||||
base::flat_set<PeerId> groupStickersSectionHidden;
|
||||
qint32 thirdSectionInfoEnabled = 0;
|
||||
qint32 smallDialogsList = 0;
|
||||
float64 dialogsWidthRatio = _variables.dialogsWidthRatio.current();
|
||||
int thirdColumnWidth = _variables.thirdColumnWidth.current();
|
||||
int thirdSectionExtendedBy = _variables.thirdSectionExtendedBy;
|
||||
stream >> selectorTab;
|
||||
stream >> lastSeenWarningSeen;
|
||||
if (!stream.atEnd()) {
|
||||
@@ -122,6 +136,21 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
stream >> thirdSectionInfoEnabled;
|
||||
stream >> smallDialogsList;
|
||||
}
|
||||
if (!stream.atEnd()) {
|
||||
qint32 value = 0;
|
||||
stream >> value;
|
||||
dialogsWidthRatio = snap(value / 1000000., 0., 1.);
|
||||
|
||||
stream >> value;
|
||||
thirdColumnWidth = value;
|
||||
|
||||
stream >> value;
|
||||
thirdSectionExtendedBy = value;
|
||||
}
|
||||
if (stream.status() != QDataStream::Ok) {
|
||||
LOG(("App Error: Bad data for AuthSessionData::constructFromSerialized()"));
|
||||
return;
|
||||
@@ -151,6 +180,132 @@ void AuthSessionData::constructFromSerialized(const QByteArray &serialized) {
|
||||
case RectPart::BottomRight: _variables.floatPlayerCorner = uncheckedCorner; break;
|
||||
}
|
||||
_variables.groupStickersSectionHidden = std::move(groupStickersSectionHidden);
|
||||
_variables.thirdSectionInfoEnabled = thirdSectionInfoEnabled;
|
||||
_variables.smallDialogsList = smallDialogsList;
|
||||
_variables.dialogsWidthRatio = dialogsWidthRatio;
|
||||
_variables.thirdColumnWidth = thirdColumnWidth;
|
||||
_variables.thirdSectionExtendedBy = thirdSectionExtendedBy;
|
||||
if (_variables.thirdSectionInfoEnabled) {
|
||||
_variables.tabbedSelectorSectionEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void AuthSessionData::markItemLayoutChanged(not_null<const HistoryItem*> item) {
|
||||
_itemLayoutChanged.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemLayoutChanged() const {
|
||||
return _itemLayoutChanged.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::requestItemRepaint(not_null<const HistoryItem*> item) {
|
||||
_itemRepaintRequest.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRepaintRequest() const {
|
||||
return _itemRepaintRequest.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markItemRemoved(not_null<const HistoryItem*> item) {
|
||||
_itemRemoved.fire_copy(item);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> AuthSessionData::itemRemoved() const {
|
||||
return _itemRemoved.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markHistoryUnloaded(not_null<const History*> history) {
|
||||
_historyUnloaded.fire_copy(history);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const History*>> AuthSessionData::historyUnloaded() const {
|
||||
return _historyUnloaded.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markHistoryCleared(not_null<const History*> history) {
|
||||
_historyCleared.fire_copy(history);
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const History*>> AuthSessionData::historyCleared() const {
|
||||
return _historyCleared.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::removeMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantRemoved.fire({ channel, user });
|
||||
}
|
||||
|
||||
auto AuthSessionData::megagroupParticipantRemoved() const -> rpl::producer<MegagroupParticipant> {
|
||||
return _megagroupParticipantRemoved.events();
|
||||
}
|
||||
|
||||
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantRemoved(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantRemoved()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
void AuthSessionData::addNewMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user) {
|
||||
_megagroupParticipantAdded.fire({ channel, user });
|
||||
}
|
||||
|
||||
auto AuthSessionData::megagroupParticipantAdded() const -> rpl::producer<MegagroupParticipant> {
|
||||
return _megagroupParticipantAdded.events();
|
||||
}
|
||||
|
||||
rpl::producer<not_null<UserData*>> AuthSessionData::megagroupParticipantAdded(
|
||||
not_null<ChannelData*> channel) const {
|
||||
return megagroupParticipantAdded()
|
||||
| rpl::filter([channel](auto updateChannel, auto user) {
|
||||
return (updateChannel == channel);
|
||||
})
|
||||
| rpl::map([](auto updateChannel, auto user) {
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
void AuthSessionData::setTabbedSelectorSectionEnabled(bool enabled) {
|
||||
_variables.tabbedSelectorSectionEnabled = enabled;
|
||||
if (enabled) {
|
||||
setThirdSectionInfoEnabled(false);
|
||||
}
|
||||
setTabbedReplacedWithInfo(false);
|
||||
}
|
||||
|
||||
rpl::producer<bool> AuthSessionData::tabbedReplacedWithInfoValue() const {
|
||||
return _tabbedReplacedWithInfoValue.events_starting_with(
|
||||
tabbedReplacedWithInfo());
|
||||
}
|
||||
|
||||
void AuthSessionData::setThirdSectionInfoEnabled(bool enabled) {
|
||||
if (_variables.thirdSectionInfoEnabled != enabled) {
|
||||
_variables.thirdSectionInfoEnabled = enabled;
|
||||
if (enabled) {
|
||||
setTabbedSelectorSectionEnabled(false);
|
||||
}
|
||||
setTabbedReplacedWithInfo(false);
|
||||
_thirdSectionInfoEnabledValue.fire_copy(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<bool> AuthSessionData::thirdSectionInfoEnabledValue() const {
|
||||
return _thirdSectionInfoEnabledValue.events_starting_with(
|
||||
thirdSectionInfoEnabled());
|
||||
}
|
||||
|
||||
void AuthSessionData::setTabbedReplacedWithInfo(bool enabled) {
|
||||
if (_tabbedReplacedWithInfo != enabled) {
|
||||
_tabbedReplacedWithInfo = enabled;
|
||||
_tabbedReplacedWithInfoValue.fire_copy(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
QString AuthSessionData::getSoundPath(const QString &key) const {
|
||||
@@ -161,6 +316,68 @@ QString AuthSessionData::getSoundPath(const QString &key) const {
|
||||
return qsl(":/sounds/") + key + qsl(".mp3");
|
||||
}
|
||||
|
||||
void AuthSessionData::setDialogsWidthRatio(float64 ratio) {
|
||||
_variables.dialogsWidthRatio = ratio;
|
||||
}
|
||||
|
||||
float64 AuthSessionData::dialogsWidthRatio() const {
|
||||
return _variables.dialogsWidthRatio.current();
|
||||
}
|
||||
|
||||
rpl::producer<float64> AuthSessionData::dialogsWidthRatioChanges() const {
|
||||
return _variables.dialogsWidthRatio.changes();
|
||||
}
|
||||
|
||||
void AuthSessionData::setThirdColumnWidth(int width) {
|
||||
_variables.thirdColumnWidth = width;
|
||||
}
|
||||
|
||||
int AuthSessionData::thirdColumnWidth() const {
|
||||
return _variables.thirdColumnWidth.current();
|
||||
}
|
||||
|
||||
rpl::producer<int> AuthSessionData::thirdColumnWidthChanges() const {
|
||||
return _variables.thirdColumnWidth.changes();
|
||||
}
|
||||
|
||||
void AuthSessionData::markStickersUpdated() {
|
||||
_stickersUpdated.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> AuthSessionData::stickersUpdated() const {
|
||||
return _stickersUpdated.events();
|
||||
}
|
||||
|
||||
void AuthSessionData::markSavedGifsUpdated() {
|
||||
_savedGifsUpdated.fire({});
|
||||
}
|
||||
|
||||
rpl::producer<> AuthSessionData::savedGifsUpdated() const {
|
||||
return _savedGifsUpdated.events();
|
||||
}
|
||||
|
||||
HistoryItemsList AuthSessionData::idsToItems(
|
||||
const MessageIdsList &ids) const {
|
||||
return ranges::view::all(
|
||||
ids
|
||||
) | ranges::view::transform([](const FullMsgId &fullId) {
|
||||
return App::histItemById(fullId);
|
||||
}) | ranges::view::filter([](HistoryItem *item) {
|
||||
return item != nullptr;
|
||||
}) | ranges::view::transform([](HistoryItem *item) {
|
||||
return not_null<HistoryItem*>(item);
|
||||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
MessageIdsList AuthSessionData::itemsToIds(
|
||||
const HistoryItemsList &items) const {
|
||||
return ranges::view::all(
|
||||
items
|
||||
) | ranges::view::transform([](not_null<HistoryItem*> item) {
|
||||
return item->fullId();
|
||||
}) | ranges::to_vector;
|
||||
}
|
||||
|
||||
AuthSession &Auth() {
|
||||
auto result = Messenger::Instance().authSession();
|
||||
Assert(result != nullptr);
|
||||
@@ -174,6 +391,7 @@ AuthSession::AuthSession(UserId userId)
|
||||
, _calls(std::make_unique<Calls::Instance>())
|
||||
, _downloader(std::make_unique<Storage::Downloader>())
|
||||
, _uploader(std::make_unique<Storage::Uploader>())
|
||||
, _storage(std::make_unique<Storage::Facade>())
|
||||
, _notifications(std::make_unique<Window::Notifications::System>(this)) {
|
||||
Expects(_userId != 0);
|
||||
_saveDataTimer.setCallback([this] {
|
||||
|
||||
@@ -20,11 +20,16 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <rpl/event_stream.h>
|
||||
#include <rpl/filter.h>
|
||||
#include <rpl/variable.h>
|
||||
#include "base/timer.h"
|
||||
#include "chat_helpers/stickers.h"
|
||||
|
||||
namespace Storage {
|
||||
class Downloader;
|
||||
class Uploader;
|
||||
class Facade;
|
||||
} // namespace Storage
|
||||
|
||||
namespace Window {
|
||||
@@ -55,18 +60,6 @@ public:
|
||||
base::Observable<void> &moreChatsLoaded() {
|
||||
return _moreChatsLoaded;
|
||||
}
|
||||
base::Observable<void> &stickersUpdated() {
|
||||
return _stickersUpdated;
|
||||
}
|
||||
base::Observable<void> &savedGifsUpdated() {
|
||||
return _savedGifsUpdated;
|
||||
}
|
||||
base::Observable<not_null<History*>> &historyCleared() {
|
||||
return _historyCleared;
|
||||
}
|
||||
base::Observable<not_null<const HistoryItem*>> &repaintLogEntry() {
|
||||
return _repaintLogEntry;
|
||||
}
|
||||
base::Observable<void> &pendingHistoryResize() {
|
||||
return _pendingHistoryResize;
|
||||
}
|
||||
@@ -77,9 +70,34 @@ public:
|
||||
base::Observable<ItemVisibilityQuery> &queryItemVisibility() {
|
||||
return _queryItemVisibility;
|
||||
}
|
||||
void markItemLayoutChanged(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemLayoutChanged() const;
|
||||
void requestItemRepaint(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRepaintRequest() const;
|
||||
void markItemRemoved(not_null<const HistoryItem*> item);
|
||||
rpl::producer<not_null<const HistoryItem*>> itemRemoved() const;
|
||||
void markHistoryUnloaded(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyUnloaded() const;
|
||||
void markHistoryCleared(not_null<const History*> history);
|
||||
rpl::producer<not_null<const History*>> historyCleared() const;
|
||||
using MegagroupParticipant = std::tuple<
|
||||
not_null<ChannelData*>,
|
||||
not_null<UserData*>>;
|
||||
void removeMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<MegagroupParticipant> megagroupParticipantRemoved() const;
|
||||
rpl::producer<not_null<UserData*>> megagroupParticipantRemoved(
|
||||
not_null<ChannelData*> channel) const;
|
||||
void addNewMegagroupParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
not_null<UserData*> user);
|
||||
rpl::producer<MegagroupParticipant> megagroupParticipantAdded() const;
|
||||
rpl::producer<not_null<UserData*>> megagroupParticipantAdded(
|
||||
not_null<ChannelData*> channel) const;
|
||||
|
||||
void copyFrom(const AuthSessionData &other) {
|
||||
_variables = other._variables;
|
||||
void moveFrom(AuthSessionData &&other) {
|
||||
_variables = std::move(other._variables);
|
||||
}
|
||||
QByteArray serialize() const;
|
||||
void constructFromSerialized(const QByteArray &serialized);
|
||||
@@ -99,8 +117,28 @@ public:
|
||||
bool tabbedSelectorSectionEnabled() const {
|
||||
return _variables.tabbedSelectorSectionEnabled;
|
||||
}
|
||||
void setTabbedSelectorSectionEnabled(bool enabled) {
|
||||
_variables.tabbedSelectorSectionEnabled = enabled;
|
||||
void setTabbedSelectorSectionEnabled(bool enabled);
|
||||
bool thirdSectionInfoEnabled() const {
|
||||
return _variables.thirdSectionInfoEnabled;
|
||||
}
|
||||
void setThirdSectionInfoEnabled(bool enabled);
|
||||
rpl::producer<bool> thirdSectionInfoEnabledValue() const;
|
||||
int thirdSectionExtendedBy() const {
|
||||
return _variables.thirdSectionExtendedBy;
|
||||
}
|
||||
void setThirdSectionExtendedBy(int savedValue) {
|
||||
_variables.thirdSectionExtendedBy = savedValue;
|
||||
}
|
||||
bool tabbedReplacedWithInfo() const {
|
||||
return _tabbedReplacedWithInfo;
|
||||
}
|
||||
void setTabbedReplacedWithInfo(bool enabled);
|
||||
rpl::producer<bool> tabbedReplacedWithInfoValue() const;
|
||||
void setSmallDialogsList(bool enabled) {
|
||||
_variables.smallDialogsList = enabled;
|
||||
}
|
||||
bool smallDialogsList() const {
|
||||
return _variables.smallDialogsList;
|
||||
}
|
||||
void setLastTimeVideoPlayedAt(TimeMs time) {
|
||||
_lastTimeVideoPlayedAt = time;
|
||||
@@ -133,6 +171,17 @@ public:
|
||||
RectPart floatPlayerCorner() const {
|
||||
return _variables.floatPlayerCorner;
|
||||
}
|
||||
void setDialogsWidthRatio(float64 ratio);
|
||||
float64 dialogsWidthRatio() const;
|
||||
rpl::producer<float64> dialogsWidthRatioChanges() const;
|
||||
void setThirdColumnWidth(int width);
|
||||
int thirdColumnWidth() const;
|
||||
rpl::producer<int> thirdColumnWidthChanges() const;
|
||||
|
||||
void markStickersUpdated();
|
||||
rpl::producer<> stickersUpdated() const;
|
||||
void markSavedGifsUpdated();
|
||||
rpl::producer<> savedGifsUpdated() const;
|
||||
void setGroupStickersSectionHidden(PeerId peerId) {
|
||||
_variables.groupStickersSectionHidden.insert(peerId);
|
||||
}
|
||||
@@ -142,30 +191,140 @@ public:
|
||||
void removeGroupStickersSectionHidden(PeerId peerId) {
|
||||
_variables.groupStickersSectionHidden.remove(peerId);
|
||||
}
|
||||
bool stickersUpdateNeeded(TimeMs now) const {
|
||||
return stickersUpdateNeeded(_lastStickersUpdate, now);
|
||||
}
|
||||
void setLastStickersUpdate(TimeMs update) {
|
||||
_lastStickersUpdate = update;
|
||||
}
|
||||
bool recentStickersUpdateNeeded(TimeMs now) const {
|
||||
return stickersUpdateNeeded(_lastRecentStickersUpdate, now);
|
||||
}
|
||||
void setLastRecentStickersUpdate(TimeMs update) {
|
||||
_lastRecentStickersUpdate = update;
|
||||
}
|
||||
bool favedStickersUpdateNeeded(TimeMs now) const {
|
||||
return stickersUpdateNeeded(_lastFavedStickersUpdate, now);
|
||||
}
|
||||
void setLastFavedStickersUpdate(TimeMs update) {
|
||||
_lastFavedStickersUpdate = update;
|
||||
}
|
||||
bool featuredStickersUpdateNeeded(TimeMs now) const {
|
||||
return stickersUpdateNeeded(_lastFeaturedStickersUpdate, now);
|
||||
}
|
||||
void setLastFeaturedStickersUpdate(TimeMs update) {
|
||||
_lastFeaturedStickersUpdate = update;
|
||||
}
|
||||
bool savedGifsUpdateNeeded(TimeMs now) const {
|
||||
return stickersUpdateNeeded(_lastSavedGifsUpdate, now);
|
||||
}
|
||||
void setLastSavedGifsUpdate(TimeMs update) {
|
||||
_lastSavedGifsUpdate = update;
|
||||
}
|
||||
int featuredStickerSetsUnreadCount() const {
|
||||
return _featuredStickerSetsUnreadCount.current();
|
||||
}
|
||||
void setFeaturedStickerSetsUnreadCount(int count) {
|
||||
_featuredStickerSetsUnreadCount = count;
|
||||
}
|
||||
rpl::producer<int> featuredStickerSetsUnreadCountValue() const {
|
||||
return _featuredStickerSetsUnreadCount.value();
|
||||
}
|
||||
const Stickers::Sets &stickerSets() const {
|
||||
return _stickerSets;
|
||||
}
|
||||
Stickers::Sets &stickerSetsRef() {
|
||||
return _stickerSets;
|
||||
}
|
||||
const Stickers::Order &stickerSetsOrder() const {
|
||||
return _stickerSetsOrder;
|
||||
}
|
||||
Stickers::Order &stickerSetsOrderRef() {
|
||||
return _stickerSetsOrder;
|
||||
}
|
||||
const Stickers::Order &featuredStickerSetsOrder() const {
|
||||
return _featuredStickerSetsOrder;
|
||||
}
|
||||
Stickers::Order &featuredStickerSetsOrderRef() {
|
||||
return _featuredStickerSetsOrder;
|
||||
}
|
||||
const Stickers::Order &archivedStickerSetsOrder() const {
|
||||
return _archivedStickerSetsOrder;
|
||||
}
|
||||
Stickers::Order &archivedStickerSetsOrderRef() {
|
||||
return _archivedStickerSetsOrder;
|
||||
}
|
||||
const Stickers::SavedGifs &savedGifs() const {
|
||||
return _savedGifs;
|
||||
}
|
||||
Stickers::SavedGifs &savedGifsRef() {
|
||||
return _savedGifs;
|
||||
}
|
||||
|
||||
HistoryItemsList idsToItems(const MessageIdsList &ids) const;
|
||||
MessageIdsList itemsToIds(const HistoryItemsList &items) const;
|
||||
|
||||
private:
|
||||
struct Variables {
|
||||
Variables();
|
||||
|
||||
static constexpr auto kDefaultDialogsWidthRatio = 5. / 14;
|
||||
static constexpr auto kDefaultThirdColumnWidth = 0;
|
||||
|
||||
bool lastSeenWarningSeen = false;
|
||||
ChatHelpers::SelectorTab selectorTab;
|
||||
bool tabbedSelectorSectionEnabled = true;
|
||||
ChatHelpers::SelectorTab selectorTab; // per-window
|
||||
bool tabbedSelectorSectionEnabled = false; // per-window
|
||||
int tabbedSelectorSectionTooltipShown = 0;
|
||||
QMap<QString, QString> soundOverrides;
|
||||
Window::Column floatPlayerColumn;
|
||||
RectPart floatPlayerCorner;
|
||||
OrderedSet<PeerId> groupStickersSectionHidden;
|
||||
Window::Column floatPlayerColumn; // per-window
|
||||
RectPart floatPlayerCorner; // per-window
|
||||
base::flat_set<PeerId> groupStickersSectionHidden;
|
||||
bool thirdSectionInfoEnabled = true; // per-window
|
||||
bool smallDialogsList = false; // per-window
|
||||
int thirdSectionExtendedBy = -1; // per-window
|
||||
rpl::variable<float64> dialogsWidthRatio
|
||||
= kDefaultDialogsWidthRatio; // per-window
|
||||
rpl::variable<int> thirdColumnWidth
|
||||
= kDefaultThirdColumnWidth; // per-window
|
||||
};
|
||||
|
||||
bool stickersUpdateNeeded(TimeMs lastUpdate, TimeMs now) const {
|
||||
constexpr auto kStickersUpdateTimeout = TimeMs(3600'000);
|
||||
return (lastUpdate == 0)
|
||||
|| (now >= lastUpdate + kStickersUpdateTimeout);
|
||||
}
|
||||
|
||||
base::Variable<bool> _contactsLoaded = { false };
|
||||
base::Variable<bool> _allChatsLoaded = { false };
|
||||
base::Observable<void> _moreChatsLoaded;
|
||||
base::Observable<void> _stickersUpdated;
|
||||
base::Observable<void> _savedGifsUpdated;
|
||||
base::Observable<not_null<History*>> _historyCleared;
|
||||
base::Observable<not_null<const HistoryItem*>> _repaintLogEntry;
|
||||
base::Observable<void> _pendingHistoryResize;
|
||||
base::Observable<ItemVisibilityQuery> _queryItemVisibility;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemLayoutChanged;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRepaintRequest;
|
||||
rpl::event_stream<not_null<const HistoryItem*>> _itemRemoved;
|
||||
rpl::event_stream<not_null<const History*>> _historyUnloaded;
|
||||
rpl::event_stream<not_null<const History*>> _historyCleared;
|
||||
rpl::event_stream<MegagroupParticipant> _megagroupParticipantRemoved;
|
||||
rpl::event_stream<MegagroupParticipant> _megagroupParticipantAdded;
|
||||
|
||||
rpl::event_stream<> _stickersUpdated;
|
||||
rpl::event_stream<> _savedGifsUpdated;
|
||||
TimeMs _lastStickersUpdate = 0;
|
||||
TimeMs _lastRecentStickersUpdate = 0;
|
||||
TimeMs _lastFavedStickersUpdate = 0;
|
||||
TimeMs _lastFeaturedStickersUpdate = 0;
|
||||
TimeMs _lastSavedGifsUpdate = 0;
|
||||
rpl::variable<int> _featuredStickerSetsUnreadCount = 0;
|
||||
Stickers::Sets _stickerSets;
|
||||
Stickers::Order _stickerSetsOrder;
|
||||
Stickers::Order _featuredStickerSetsOrder;
|
||||
Stickers::Order _archivedStickerSetsOrder;
|
||||
Stickers::SavedGifs _savedGifs;
|
||||
|
||||
rpl::event_stream<bool> _thirdSectionInfoEnabledValue;
|
||||
bool _tabbedReplacedWithInfo = false;
|
||||
rpl::event_stream<bool> _tabbedReplacedWithInfoValue;
|
||||
|
||||
Variables _variables;
|
||||
TimeMs _lastTimeVideoPlayedAt = 0;
|
||||
|
||||
@@ -199,6 +358,9 @@ public:
|
||||
Storage::Uploader &uploader() {
|
||||
return *_uploader;
|
||||
}
|
||||
Storage::Facade &storage() {
|
||||
return *_storage;
|
||||
}
|
||||
|
||||
base::Observable<void> &downloaderTaskFinished();
|
||||
|
||||
@@ -209,7 +371,7 @@ public:
|
||||
AuthSessionData &data() {
|
||||
return _data;
|
||||
}
|
||||
void saveDataDelayed(TimeMs delay);
|
||||
void saveDataDelayed(TimeMs delay = kDefaultSaveDelay);
|
||||
|
||||
ApiWrap &api() {
|
||||
return *_api;
|
||||
@@ -223,11 +385,13 @@ public:
|
||||
void checkAutoLockIn(TimeMs time);
|
||||
|
||||
base::Observable<DocumentData*> documentUpdated;
|
||||
base::Observable<std::pair<HistoryItem*, MsgId>> messageIdChanging;
|
||||
base::Observable<std::pair<not_null<HistoryItem*>, MsgId>> messageIdChanging;
|
||||
|
||||
~AuthSession();
|
||||
|
||||
private:
|
||||
static constexpr auto kDefaultSaveDelay = TimeMs(1000);
|
||||
|
||||
const UserId _userId = 0;
|
||||
AuthSessionData _data;
|
||||
base::Timer _saveDataTimer;
|
||||
@@ -239,6 +403,7 @@ private:
|
||||
const std::unique_ptr<Calls::Instance> _calls;
|
||||
const std::unique_ptr<Storage::Downloader> _downloader;
|
||||
const std::unique_ptr<Storage::Uploader> _uploader;
|
||||
const std::unique_ptr<Storage::Facade> _storage;
|
||||
const std::unique_ptr<Window::Notifications::System> _notifications;
|
||||
|
||||
};
|
||||
|
||||
@@ -22,19 +22,19 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Range, typename Method>
|
||||
decltype(auto) for_each(Range &&range, Method &&method) {
|
||||
return std::for_each(
|
||||
std::begin(std::forward<Range>(range)),
|
||||
std::end(std::forward<Range>(range)),
|
||||
std::forward<Method>(method));
|
||||
template <typename Type>
|
||||
inline Type take(Type &value) {
|
||||
return std::exchange(value, Type {});
|
||||
}
|
||||
|
||||
template <typename Method>
|
||||
decltype(auto) for_each_apply(Method &&method) {
|
||||
return [&method](auto &&range) {
|
||||
return for_each(std::forward<decltype(range)>(range), std::forward<Method>(method));
|
||||
};
|
||||
template <typename Type>
|
||||
inline Type duplicate(const Type &value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename Type, size_t Size>
|
||||
inline constexpr size_t array_size(const Type(&)[Size]) {
|
||||
return Size;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
||||
63
Telegram/SourceFiles/base/algorithm_tests.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "catch.hpp"
|
||||
|
||||
#include "base/index_based_iterator.h"
|
||||
|
||||
TEST_CASE("index_based_iterator tests", "[base::algorithm]") {
|
||||
auto v = std::vector<int>();
|
||||
|
||||
v.insert(v.end(), { 1, 2, 3, 4, 5, 4, 3, 2, 1 });
|
||||
auto push_back_safe_remove_if = [](auto &v, auto predicate) {
|
||||
auto begin = base::index_based_begin(v);
|
||||
auto end = base::index_based_end(v);
|
||||
auto from = std::remove_if(begin, end, predicate);
|
||||
if (from != end) {
|
||||
auto newEnd = base::index_based_end(v);
|
||||
if (newEnd != end) {
|
||||
REQUIRE(newEnd > end);
|
||||
while (end != newEnd) {
|
||||
*from++ = *end++;
|
||||
}
|
||||
}
|
||||
v.erase(from.base(), newEnd.base());
|
||||
}
|
||||
};
|
||||
SECTION("allows to push_back from predicate") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
v.push_back(value);
|
||||
return (value % 2) == 1;
|
||||
});
|
||||
auto expected = std::vector<int> { 2, 4, 4, 2, 1, 2, 3, 4, 5, 4, 3, 2, 1 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
|
||||
SECTION("allows to push_back while removing all") {
|
||||
push_back_safe_remove_if(v, [&v](int value) {
|
||||
if (value == 5) {
|
||||
v.push_back(value);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
auto expected = std::vector<int> { 5 };
|
||||
REQUIRE(v == expected);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ namespace assertion {
|
||||
void log(const char *message, const char *file, int line);
|
||||
|
||||
// Release build assertions.
|
||||
inline void noop() {
|
||||
inline constexpr void noop() {
|
||||
}
|
||||
|
||||
[[noreturn]] inline void fail(const char *message, const char *file, int line) {
|
||||
@@ -43,10 +43,20 @@ inline void noop() {
|
||||
std::abort();
|
||||
}
|
||||
|
||||
inline void validate(bool condition, const char *message, const char *file, int line) {
|
||||
#ifndef GSL_UNLIKELY
|
||||
#define DEFINED_GSL_UNLIKELY_
|
||||
#define GSL_UNLIKELY(expression) (expression)
|
||||
#endif // GSL_UNLIKELY
|
||||
|
||||
inline constexpr void validate(bool condition, const char *message, const char *file, int line) {
|
||||
(GSL_UNLIKELY(!(condition))) ? fail(message, file, line) : noop();
|
||||
}
|
||||
|
||||
#ifdef DEFINED_GSL_UNLIKELY_
|
||||
#undef GSL_UNLIKELY
|
||||
#undef DEFINED_GSL_UNLIKELY_
|
||||
#endif // DEFINED_GSL_UNLIKELY_
|
||||
|
||||
} // namespace assertion
|
||||
} // namespace base
|
||||
|
||||
|
||||
@@ -64,12 +64,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#error Please add support for your architecture in base/build_config.h
|
||||
#endif
|
||||
|
||||
#if defined(COMPILER_GCC) || defined(COMPILER_CLANG)
|
||||
#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
|
||||
#else
|
||||
#define WARN_UNUSED_RESULT
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#define FORCE_INLINE inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
|
||||
60
Telegram/SourceFiles/base/enum_mask.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Enum>
|
||||
class enum_mask {
|
||||
using Type = std::uint32_t;
|
||||
|
||||
public:
|
||||
static_assert(static_cast<int>(Enum::kCount) <= 32, "We have only 32 bit.");
|
||||
|
||||
enum_mask() = default;
|
||||
enum_mask(Enum value) : _value(ToBit(value)) {
|
||||
}
|
||||
|
||||
enum_mask added(enum_mask other) const {
|
||||
auto result = *this;
|
||||
result.set(other);
|
||||
return result;
|
||||
}
|
||||
void set(enum_mask other) {
|
||||
_value |= other._value;
|
||||
}
|
||||
bool test(Enum value) const {
|
||||
return _value & ToBit(value);
|
||||
}
|
||||
|
||||
explicit operator bool() const {
|
||||
return _value != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
inline static Type ToBit(Enum value) {
|
||||
return 1 << static_cast<Type>(value);
|
||||
}
|
||||
Type _value = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
@@ -22,12 +22,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#if defined _MSC_VER && _MSC_VER < 1910
|
||||
#define FLAGS_CONSTEXPR
|
||||
#else // MSVS2015
|
||||
#define FLAGS_CONSTEXPR constexpr
|
||||
#endif // MSVS2015
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename EnumType>
|
||||
@@ -69,9 +63,11 @@ public:
|
||||
constexpr flags() = default;
|
||||
constexpr flags(details::flags_zero_helper) noexcept {
|
||||
}
|
||||
constexpr flags(Enum value) noexcept : _value(static_cast<Type>(value)) {
|
||||
constexpr flags(Enum value) noexcept
|
||||
: _value(static_cast<Type>(value)) {
|
||||
}
|
||||
explicit constexpr flags(Type value) noexcept : _value(value) {
|
||||
static constexpr flags from_raw(Type value) noexcept {
|
||||
return flags(static_cast<Enum>(value));
|
||||
}
|
||||
|
||||
constexpr auto value() const noexcept {
|
||||
@@ -81,40 +77,40 @@ public:
|
||||
return value();
|
||||
}
|
||||
|
||||
FLAGS_CONSTEXPR auto &operator|=(flags b) noexcept {
|
||||
constexpr auto &operator|=(flags b) noexcept {
|
||||
_value |= b.value();
|
||||
return *this;
|
||||
}
|
||||
FLAGS_CONSTEXPR auto &operator&=(flags b) noexcept {
|
||||
constexpr auto &operator&=(flags b) noexcept {
|
||||
_value &= b.value();
|
||||
return *this;
|
||||
}
|
||||
FLAGS_CONSTEXPR auto &operator^=(flags b) noexcept {
|
||||
constexpr auto &operator^=(flags b) noexcept {
|
||||
_value ^= b.value();
|
||||
return *this;
|
||||
}
|
||||
|
||||
FLAGS_CONSTEXPR auto operator~() const noexcept {
|
||||
return flags(~value());
|
||||
constexpr auto operator~() const noexcept {
|
||||
return from_raw(~value());
|
||||
}
|
||||
|
||||
FLAGS_CONSTEXPR auto operator|(flags b) const noexcept {
|
||||
constexpr auto operator|(flags b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
FLAGS_CONSTEXPR auto operator&(flags b) const noexcept {
|
||||
constexpr auto operator&(flags b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
FLAGS_CONSTEXPR auto operator^(flags b) const noexcept {
|
||||
constexpr auto operator^(flags b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
FLAGS_CONSTEXPR auto operator|(Enum b) const noexcept {
|
||||
constexpr auto operator|(Enum b) const noexcept {
|
||||
return (flags(*this) |= b);
|
||||
}
|
||||
FLAGS_CONSTEXPR auto operator&(Enum b) const noexcept {
|
||||
constexpr auto operator&(Enum b) const noexcept {
|
||||
return (flags(*this) &= b);
|
||||
}
|
||||
FLAGS_CONSTEXPR auto operator^(Enum b) const noexcept {
|
||||
constexpr auto operator^(Enum b) const noexcept {
|
||||
return (flags(*this) ^= b);
|
||||
}
|
||||
|
||||
@@ -296,8 +292,6 @@ inline constexpr auto operator>=(ExtendedEnum a, flags<extended_flags_t<Extended
|
||||
|
||||
} // namespace base
|
||||
|
||||
#undef FLAGS_CONSTEXPR
|
||||
|
||||
template <typename Enum,
|
||||
typename = std::enable_if_t<std::is_enum<Enum>::value>,
|
||||
typename = std::enable_if_t<is_flag_type(Enum{})>>
|
||||
|
||||
@@ -21,149 +21,267 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
#include "base/optional.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Key, typename Type>
|
||||
template <
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename Compare = std::less<>>
|
||||
class flat_map;
|
||||
|
||||
template <typename Key, typename Type>
|
||||
template <
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename Compare = std::less<>>
|
||||
class flat_multi_map;
|
||||
|
||||
template <typename Key, typename Type, typename iterator_impl, typename pointer_impl, typename reference_impl>
|
||||
template <
|
||||
typename Me,
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename iterator_impl,
|
||||
typename pointer_impl,
|
||||
typename reference_impl>
|
||||
class flat_multi_map_iterator_base_impl;
|
||||
|
||||
template <typename Key, typename Type, typename iterator_impl, typename pointer_impl, typename reference_impl>
|
||||
template <typename Key, typename Value>
|
||||
struct flat_multi_map_pair_type {
|
||||
using first_type = const Key;
|
||||
using second_type = Value;
|
||||
|
||||
constexpr flat_multi_map_pair_type()
|
||||
: first()
|
||||
, second() {
|
||||
}
|
||||
|
||||
template <typename OtherKey, typename OtherValue>
|
||||
constexpr flat_multi_map_pair_type(OtherKey &&key, OtherValue &&value)
|
||||
: first(std::forward<OtherKey>(key))
|
||||
, second(std::forward<OtherValue>(value)) {
|
||||
}
|
||||
|
||||
flat_multi_map_pair_type(const flat_multi_map_pair_type&) = default;
|
||||
flat_multi_map_pair_type(flat_multi_map_pair_type&&) = default;
|
||||
|
||||
flat_multi_map_pair_type &operator=(const flat_multi_map_pair_type&) = delete;
|
||||
flat_multi_map_pair_type &operator=(flat_multi_map_pair_type &&other) {
|
||||
const_cast<Key&>(first) = other.first;
|
||||
second = std::move(other.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(flat_multi_map_pair_type &other) {
|
||||
using std::swap;
|
||||
|
||||
if (this != &other) {
|
||||
std::swap(
|
||||
const_cast<Key&>(first),
|
||||
const_cast<Key&>(other.first));
|
||||
std::swap(second, other.second);
|
||||
}
|
||||
}
|
||||
|
||||
const Key first;
|
||||
Value second;
|
||||
};
|
||||
|
||||
template <
|
||||
typename Me,
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename iterator_impl,
|
||||
typename pointer_impl,
|
||||
typename reference_impl>
|
||||
class flat_multi_map_iterator_base_impl {
|
||||
public:
|
||||
using iterator_category = typename iterator_impl::iterator_category;
|
||||
|
||||
using value_type = typename flat_multi_map<Key, Type>::value_type;
|
||||
using pair_type = flat_multi_map_pair_type<Key, Type>;
|
||||
using value_type = pair_type;
|
||||
using difference_type = typename iterator_impl::difference_type;
|
||||
using pointer = pointer_impl;
|
||||
using const_pointer = typename flat_multi_map<Key, Type>::const_pointer;
|
||||
using reference = reference_impl;
|
||||
using const_reference = typename flat_multi_map<Key, Type>::const_reference;
|
||||
|
||||
flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl()) : _impl(impl) {
|
||||
flat_multi_map_iterator_base_impl(iterator_impl impl = iterator_impl())
|
||||
: _impl(impl) {
|
||||
}
|
||||
|
||||
reference operator*() {
|
||||
reference operator*() const {
|
||||
return *_impl;
|
||||
}
|
||||
const_reference operator*() const {
|
||||
return *_impl;
|
||||
}
|
||||
pointer operator->() {
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
const_pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
flat_multi_map_iterator_base_impl &operator++() {
|
||||
Me &operator++() {
|
||||
++_impl;
|
||||
return *this;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
flat_multi_map_iterator_base_impl operator++(int) {
|
||||
Me operator++(int) {
|
||||
return _impl++;
|
||||
}
|
||||
flat_multi_map_iterator_base_impl &operator--() {
|
||||
Me &operator--() {
|
||||
--_impl;
|
||||
return *this;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
flat_multi_map_iterator_base_impl operator--(int) {
|
||||
Me operator--(int) {
|
||||
return _impl--;
|
||||
}
|
||||
flat_multi_map_iterator_base_impl &operator+=(difference_type offset) {
|
||||
Me &operator+=(difference_type offset) {
|
||||
_impl += offset;
|
||||
return *this;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
flat_multi_map_iterator_base_impl operator+(difference_type offset) const {
|
||||
Me operator+(difference_type offset) const {
|
||||
return _impl + offset;
|
||||
}
|
||||
flat_multi_map_iterator_base_impl &operator-=(difference_type offset) {
|
||||
Me &operator-=(difference_type offset) {
|
||||
_impl -= offset;
|
||||
return *this;
|
||||
return static_cast<Me&>(*this);
|
||||
}
|
||||
flat_multi_map_iterator_base_impl operator-(difference_type offset) const {
|
||||
Me operator-(difference_type offset) const {
|
||||
return _impl - offset;
|
||||
}
|
||||
difference_type operator-(const flat_multi_map_iterator_base_impl &right) const {
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
difference_type operator-(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl - right._impl;
|
||||
}
|
||||
reference operator[](difference_type offset) {
|
||||
return _impl[offset];
|
||||
}
|
||||
const_reference operator[](difference_type offset) const {
|
||||
reference operator[](difference_type offset) const {
|
||||
return _impl[offset];
|
||||
}
|
||||
|
||||
bool operator==(const flat_multi_map_iterator_base_impl &right) const {
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator==(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl == right._impl;
|
||||
}
|
||||
bool operator!=(const flat_multi_map_iterator_base_impl &right) const {
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator!=(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl != right._impl;
|
||||
}
|
||||
bool operator<(const flat_multi_map_iterator_base_impl &right) const {
|
||||
template <
|
||||
typename other_me,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
bool operator<(
|
||||
const flat_multi_map_iterator_base_impl<
|
||||
other_me,
|
||||
Key,
|
||||
Type,
|
||||
other_iterator_impl,
|
||||
other_pointer_impl,
|
||||
other_reference_impl> &right) const {
|
||||
return _impl < right._impl;
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_impl _impl;
|
||||
friend class flat_multi_map<Key, Type>;
|
||||
|
||||
template <
|
||||
typename OtherKey,
|
||||
typename OtherType,
|
||||
typename OtherCompare>
|
||||
friend class flat_multi_map;
|
||||
|
||||
template <
|
||||
typename OtherMe,
|
||||
typename OtherKey,
|
||||
typename OtherType,
|
||||
typename other_iterator_impl,
|
||||
typename other_pointer_impl,
|
||||
typename other_reference_impl>
|
||||
friend class flat_multi_map_iterator_base_impl;
|
||||
|
||||
};
|
||||
|
||||
template <typename Key, typename Type>
|
||||
template <typename Key, typename Type, typename Compare>
|
||||
class flat_multi_map {
|
||||
using self = flat_multi_map<Key, Type>;
|
||||
class key_const_wrap {
|
||||
public:
|
||||
key_const_wrap(const Key &value) : _value(value) {
|
||||
}
|
||||
key_const_wrap(Key &&value) : _value(std::move(value)) {
|
||||
}
|
||||
inline operator const Key&() const {
|
||||
return _value;
|
||||
}
|
||||
public:
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
class reverse_iterator;
|
||||
class const_reverse_iterator;
|
||||
|
||||
friend inline bool operator<(const Key &a, const key_const_wrap &b) {
|
||||
return a < ((const Key&)b);
|
||||
}
|
||||
friend inline bool operator<(const key_const_wrap &a, const Key &b) {
|
||||
return ((const Key&)a) < b;
|
||||
}
|
||||
friend inline bool operator<(const key_const_wrap &a, const key_const_wrap &b) {
|
||||
return ((const Key&)a) < ((const Key&)b);
|
||||
}
|
||||
private:
|
||||
using pair_type = flat_multi_map_pair_type<Key, Type>;
|
||||
using impl_t = std::deque<pair_type>;
|
||||
|
||||
private:
|
||||
Key _value;
|
||||
|
||||
};
|
||||
|
||||
using pair_type = std::pair<key_const_wrap, Type>;
|
||||
using impl = std::deque<pair_type>;
|
||||
|
||||
using iterator_base = flat_multi_map_iterator_base_impl<Key, Type, typename impl::iterator, pair_type*, pair_type&>;
|
||||
using const_iterator_base = flat_multi_map_iterator_base_impl<Key, Type, typename impl::const_iterator, const pair_type*, const pair_type&>;
|
||||
using reverse_iterator_base = flat_multi_map_iterator_base_impl<Key, Type, typename impl::reverse_iterator, pair_type*, pair_type&>;
|
||||
using const_reverse_iterator_base = flat_multi_map_iterator_base_impl<Key, Type, typename impl::const_reverse_iterator, const pair_type*, const pair_type&>;
|
||||
using iterator_base = flat_multi_map_iterator_base_impl<
|
||||
iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::iterator,
|
||||
pair_type*,
|
||||
pair_type&>;
|
||||
using const_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
const_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::const_iterator,
|
||||
const pair_type*,
|
||||
const pair_type&>;
|
||||
using reverse_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
reverse_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::reverse_iterator,
|
||||
pair_type*,
|
||||
pair_type&>;
|
||||
using const_reverse_iterator_base = flat_multi_map_iterator_base_impl<
|
||||
const_reverse_iterator,
|
||||
Key,
|
||||
Type,
|
||||
typename impl_t::const_reverse_iterator,
|
||||
const pair_type*,
|
||||
const pair_type&>;
|
||||
|
||||
public:
|
||||
using value_type = pair_type;
|
||||
using size_type = typename impl::size_type;
|
||||
using difference_type = typename impl::difference_type;
|
||||
using size_type = typename impl_t::size_type;
|
||||
using difference_type = typename impl_t::difference_type;
|
||||
using pointer = pair_type*;
|
||||
using const_pointer = const pair_type*;
|
||||
using reference = pair_type&;
|
||||
using const_reference = const pair_type&;
|
||||
|
||||
class const_iterator;
|
||||
class iterator : public iterator_base {
|
||||
public:
|
||||
using iterator_base::iterator_base;
|
||||
iterator() = default;
|
||||
iterator(const iterator_base &other) : iterator_base(other) {
|
||||
}
|
||||
friend class const_iterator;
|
||||
@@ -172,16 +290,17 @@ public:
|
||||
class const_iterator : public const_iterator_base {
|
||||
public:
|
||||
using const_iterator_base::const_iterator_base;
|
||||
const_iterator() = default;
|
||||
const_iterator(const_iterator_base other) : const_iterator_base(other) {
|
||||
}
|
||||
const_iterator(const iterator &other) : const_iterator_base(other._impl) {
|
||||
}
|
||||
|
||||
};
|
||||
class const_reverse_iterator;
|
||||
class reverse_iterator : public reverse_iterator_base {
|
||||
public:
|
||||
using reverse_iterator_base::reverse_iterator_base;
|
||||
reverse_iterator() = default;
|
||||
reverse_iterator(reverse_iterator_base other) : reverse_iterator_base(other) {
|
||||
}
|
||||
friend class const_reverse_iterator;
|
||||
@@ -190,6 +309,7 @@ public:
|
||||
class const_reverse_iterator : public const_reverse_iterator_base {
|
||||
public:
|
||||
using const_reverse_iterator_base::const_reverse_iterator_base;
|
||||
const_reverse_iterator() = default;
|
||||
const_reverse_iterator(const_reverse_iterator_base other) : const_reverse_iterator_base(other) {
|
||||
}
|
||||
const_reverse_iterator(const reverse_iterator &other) : const_reverse_iterator_base(other._impl) {
|
||||
@@ -198,50 +318,50 @@ public:
|
||||
};
|
||||
|
||||
size_type size() const {
|
||||
return _impl.size();
|
||||
return impl().size();
|
||||
}
|
||||
bool empty() const {
|
||||
return _impl.empty();
|
||||
return impl().empty();
|
||||
}
|
||||
void clear() {
|
||||
_impl.clear();
|
||||
impl().clear();
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return _impl.begin();
|
||||
return impl().begin();
|
||||
}
|
||||
iterator end() {
|
||||
return _impl.end();
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return _impl.begin();
|
||||
return impl().begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return _impl.end();
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator cbegin() const {
|
||||
return _impl.cbegin();
|
||||
return impl().cbegin();
|
||||
}
|
||||
const_iterator cend() const {
|
||||
return _impl.cend();
|
||||
return impl().cend();
|
||||
}
|
||||
reverse_iterator rbegin() {
|
||||
return _impl.rbegin();
|
||||
return impl().rbegin();
|
||||
}
|
||||
reverse_iterator rend() {
|
||||
return _impl.rend();
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator rbegin() const {
|
||||
return _impl.rbegin();
|
||||
return impl().rbegin();
|
||||
}
|
||||
const_reverse_iterator rend() const {
|
||||
return _impl.rend();
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator crbegin() const {
|
||||
return _impl.crbegin();
|
||||
return impl().crbegin();
|
||||
}
|
||||
const_reverse_iterator crend() const {
|
||||
return _impl.crend();
|
||||
return impl().crend();
|
||||
}
|
||||
|
||||
reference front() {
|
||||
@@ -258,26 +378,26 @@ public:
|
||||
}
|
||||
|
||||
iterator insert(const value_type &value) {
|
||||
if (empty() || (value.first < front().first)) {
|
||||
_impl.push_front(value);
|
||||
if (empty() || compare()(value.first, front().first)) {
|
||||
impl().push_front(value);
|
||||
return begin();
|
||||
} else if (!(value.first < back().first)) {
|
||||
_impl.push_back(value);
|
||||
} else if (!compare()(value.first, back().first)) {
|
||||
impl().push_back(value);
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value.first);
|
||||
return _impl.insert(where, value);
|
||||
return impl().insert(where, value);
|
||||
}
|
||||
iterator insert(value_type &&value) {
|
||||
if (empty() || (value.first < front().first)) {
|
||||
_impl.push_front(std::move(value));
|
||||
if (empty() || compare()(value.first, front().first)) {
|
||||
impl().push_front(std::move(value));
|
||||
return begin();
|
||||
} else if (!(value.first < back().first)) {
|
||||
_impl.push_back(std::move(value));
|
||||
} else if (!compare()(value.first, back().first)) {
|
||||
impl().push_back(std::move(value));
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value.first);
|
||||
return _impl.insert(where, std::move(value));
|
||||
return impl().insert(where, std::move(value));
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
@@ -285,56 +405,66 @@ public:
|
||||
}
|
||||
|
||||
bool removeOne(const Key &key) {
|
||||
if (empty() || (key < front().first) || (back().first < key)) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return false;
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
if (key < where->first) {
|
||||
if (compare()(key, where->first)) {
|
||||
return false;
|
||||
}
|
||||
_impl.erase(where);
|
||||
impl().erase(where);
|
||||
return true;
|
||||
}
|
||||
int removeAll(const Key &key) {
|
||||
if (empty() || (key < front().first) || (back().first < key)) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(key);
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
_impl.erase(range.first, range.second);
|
||||
impl().erase(range.first, range.second);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
|
||||
iterator erase(iterator where) {
|
||||
return _impl.erase(where._impl);
|
||||
iterator erase(const_iterator where) {
|
||||
return impl().erase(where._impl);
|
||||
}
|
||||
iterator erase(iterator from, iterator till) {
|
||||
return _impl.erase(from._impl, till._impl);
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
|
||||
iterator findFirst(const Key &key) {
|
||||
if (empty() || (key < front().first) || (back().first < key)) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return (key < where->first) ? _impl.end() : where;
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
const_iterator findFirst(const Key &key) const {
|
||||
if (empty() || (key < front().first) || (back().first < key)) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(key);
|
||||
return (key < where->first) ? _impl.end() : where;
|
||||
return compare()(key, where->first) ? impl().end() : where;
|
||||
}
|
||||
|
||||
bool contains(const Key &key) const {
|
||||
return findFirst(key) != end();
|
||||
}
|
||||
int count(const Key &key) const {
|
||||
if (empty() || (key < front().first) || (back().first < key)) {
|
||||
if (empty()
|
||||
|| compare()(key, front().first)
|
||||
|| compare()(back().first, key)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(key);
|
||||
@@ -342,80 +472,225 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
impl _impl;
|
||||
friend class flat_map<Key, Type>;
|
||||
friend class flat_map<Key, Type, Compare>;
|
||||
|
||||
struct Comparator {
|
||||
inline bool operator()(const pair_type &a, const Key &b) {
|
||||
return a.first < b;
|
||||
struct transparent_compare : Compare {
|
||||
inline constexpr const Compare &initial() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
inline bool operator()(const Key &a, const pair_type &b) {
|
||||
return a < b.first;
|
||||
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType1>, pair_type> &&
|
||||
!std::is_same_v<std::decay_t<OtherType2>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType1>(a),
|
||||
std::forward<OtherType2>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const -> std::enable_if_t<
|
||||
std::is_same_v<std::decay_t<OtherType1>, pair_type> &&
|
||||
std::is_same_v<std::decay_t<OtherType2>, pair_type>, bool> {
|
||||
return initial()(a.first, b.first);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
const pair_type &a,
|
||||
OtherType &&b) const {
|
||||
return operator()(a.first, std::forward<OtherType>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, pair_type>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType &&a,
|
||||
const pair_type &b) const {
|
||||
return operator()(std::forward<OtherType>(a), b.first);
|
||||
}
|
||||
|
||||
};
|
||||
typename impl::iterator getLowerBound(const Key &key) {
|
||||
return std::lower_bound(_impl.begin(), _impl.end(), key, Comparator());
|
||||
struct Data : transparent_compare {
|
||||
template <typename ...Args>
|
||||
Data(Args &&...args)
|
||||
: elements(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
impl_t elements;
|
||||
};
|
||||
|
||||
Data _data;
|
||||
const transparent_compare &compare() const noexcept {
|
||||
return _data;
|
||||
}
|
||||
typename impl::const_iterator getLowerBound(const Key &key) const {
|
||||
return std::lower_bound(_impl.begin(), _impl.end(), key, Comparator());
|
||||
const impl_t &impl() const noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
typename impl::iterator getUpperBound(const Key &key) {
|
||||
return std::upper_bound(_impl.begin(), _impl.end(), key, Comparator());
|
||||
impl_t &impl() noexcept {
|
||||
return _data.elements;
|
||||
}
|
||||
typename impl::const_iterator getUpperBound(const Key &key) const {
|
||||
return std::upper_bound(_impl.begin(), _impl.end(), key, Comparator());
|
||||
|
||||
typename impl_t::iterator getLowerBound(const Key &key) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
std::pair<typename impl::iterator, typename impl::iterator> getEqualRange(const Key &key) {
|
||||
return std::equal_range(_impl.begin(), _impl.end(), key, Comparator());
|
||||
typename impl_t::const_iterator getLowerBound(const Key &key) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
std::pair<typename impl::const_iterator, typename impl::const_iterator> getEqualRange(const Key &key) const {
|
||||
return std::equal_range(_impl.begin(), _impl.end(), key, Comparator());
|
||||
typename impl_t::iterator getUpperBound(const Key &key) {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::const_iterator getUpperBound(const Key &key) const {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::iterator,
|
||||
typename impl_t::iterator
|
||||
> getEqualRange(const Key &key) {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::const_iterator,
|
||||
typename impl_t::const_iterator
|
||||
> getEqualRange(const Key &key) const {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
key,
|
||||
compare());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Key, typename Type>
|
||||
class flat_map : public flat_multi_map<Key, Type> {
|
||||
using parent = flat_multi_map<Key, Type>;
|
||||
template <typename Key, typename Type, typename Compare>
|
||||
class flat_map : private flat_multi_map<Key, Type, Compare> {
|
||||
using parent = flat_multi_map<Key, Type, Compare>;
|
||||
using pair_type = typename parent::pair_type;
|
||||
|
||||
public:
|
||||
using parent::parent;
|
||||
using value_type = typename parent::value_type;
|
||||
using size_type = typename parent::size_type;
|
||||
using difference_type = typename parent::difference_type;
|
||||
using pointer = typename parent::pointer;
|
||||
using const_pointer = typename parent::const_pointer;
|
||||
using reference = typename parent::reference;
|
||||
using const_reference = typename parent::const_reference;
|
||||
using iterator = typename parent::iterator;
|
||||
using const_iterator = typename parent::const_iterator;
|
||||
using value_type = typename parent::value_type;
|
||||
using reverse_iterator = typename parent::reverse_iterator;
|
||||
using const_reverse_iterator = typename parent::const_reverse_iterator;
|
||||
|
||||
iterator insert(const value_type &value) {
|
||||
if (this->empty() || (value.first < this->front().first)) {
|
||||
this->_impl.push_front(value);
|
||||
return this->begin();
|
||||
} else if (this->back().first < value.first) {
|
||||
this->_impl.push_back(value);
|
||||
return (this->end() - 1);
|
||||
using parent::parent;
|
||||
using parent::size;
|
||||
using parent::empty;
|
||||
using parent::clear;
|
||||
using parent::begin;
|
||||
using parent::end;
|
||||
using parent::cbegin;
|
||||
using parent::cend;
|
||||
using parent::rbegin;
|
||||
using parent::rend;
|
||||
using parent::crbegin;
|
||||
using parent::crend;
|
||||
using parent::front;
|
||||
using parent::back;
|
||||
using parent::erase;
|
||||
using parent::contains;
|
||||
|
||||
std::pair<iterator, bool> insert(const value_type &value) {
|
||||
if (this->empty() || this->compare()(value.first, this->front().first)) {
|
||||
this->impl().push_front(value);
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, value.first)) {
|
||||
this->impl().push_back(value);
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(value.first);
|
||||
if (value.first < where->first) {
|
||||
return this->_impl.insert(where, value);
|
||||
if (this->compare()(value.first, where->first)) {
|
||||
return { this->impl().insert(where, value), true };
|
||||
}
|
||||
return this->end();
|
||||
return { where, false };
|
||||
}
|
||||
iterator insert(value_type &&value) {
|
||||
if (this->empty() || (value.first < this->front().first)) {
|
||||
this->_impl.push_front(std::move(value));
|
||||
return this->begin();
|
||||
} else if (this->back().first < value.first) {
|
||||
this->_impl.push_back(std::move(value));
|
||||
return (this->end() - 1);
|
||||
std::pair<iterator, bool> insert(value_type &&value) {
|
||||
if (this->empty() || this->compare()(value.first, this->front().first)) {
|
||||
this->impl().push_front(std::move(value));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, value.first)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(value.first);
|
||||
if (value.first < where->first) {
|
||||
return this->_impl.insert(where, std::move(value));
|
||||
if (this->compare()(value.first, where->first)) {
|
||||
return { this->impl().insert(where, std::move(value)), true };
|
||||
}
|
||||
return this->end();
|
||||
return { where, false };
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
return this->insert(value_type(std::forward<Args>(args)...));
|
||||
std::pair<iterator, bool> emplace(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
return this->insert(value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...)));
|
||||
}
|
||||
template <typename... Args>
|
||||
std::pair<iterator, bool> try_emplace(
|
||||
const Key &key,
|
||||
Args&&... args) {
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().push_front(value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...)));
|
||||
return { this->begin(), true };
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().push_back(value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...)));
|
||||
return { this->end() - 1, true };
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (this->compare()(key, where->first)) {
|
||||
return {
|
||||
this->impl().insert(
|
||||
where,
|
||||
value_type(
|
||||
key,
|
||||
Type(std::forward<Args>(args)...))),
|
||||
true
|
||||
};
|
||||
}
|
||||
return { where, false };
|
||||
}
|
||||
|
||||
bool remove(const Key &key) {
|
||||
@@ -430,16 +705,16 @@ public:
|
||||
}
|
||||
|
||||
Type &operator[](const Key &key) {
|
||||
if (this->empty() || (key < this->front().first)) {
|
||||
this->_impl.push_front({ key, Type() });
|
||||
if (this->empty() || this->compare()(key, this->front().first)) {
|
||||
this->impl().push_front({ key, Type() });
|
||||
return this->front().second;
|
||||
} else if (this->back().first < key) {
|
||||
this->_impl.push_back({ key, Type() });
|
||||
} else if (this->compare()(this->back().first, key)) {
|
||||
this->impl().push_back({ key, Type() });
|
||||
return this->back().second;
|
||||
}
|
||||
auto where = this->getLowerBound(key);
|
||||
if (key < where->first) {
|
||||
return this->_impl.insert(where, { key, Type() })->second;
|
||||
if (this->compare()(key, where->first)) {
|
||||
return this->impl().insert(where, { key, Type() })->second;
|
||||
}
|
||||
return where->second;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#include "base/flat_map.h"
|
||||
#include <string>
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") {
|
||||
@@ -49,3 +58,28 @@ TEST_CASE("flat_maps should keep items sorted by key", "[flat_map]") {
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_maps custom comparator", "[flat_map]") {
|
||||
base::flat_map<int_wrap, string, int_wrap_comparator> v;
|
||||
v.emplace({ 0 }, "a");
|
||||
v.emplace({ 5 }, "b");
|
||||
v.emplace({ 4 }, "d");
|
||||
v.emplace({ 2 }, "e");
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(int_wrap_comparator()(prev->first, i->first));
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.emplace({ 3 }, "c");
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find({ 3 }) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,29 +21,39 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <algorithm>
|
||||
|
||||
namespace base {
|
||||
|
||||
template <typename Type>
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_set;
|
||||
|
||||
template <typename Type>
|
||||
template <typename Type, typename Compare = std::less<>>
|
||||
class flat_multi_set;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_base_impl;
|
||||
class flat_multi_set_iterator_impl;
|
||||
|
||||
template <typename Type, typename iterator_impl>
|
||||
class flat_multi_set_iterator_base_impl {
|
||||
class flat_multi_set_iterator_impl {
|
||||
public:
|
||||
using iterator_category = typename iterator_impl::iterator_category;
|
||||
|
||||
using value_type = typename flat_multi_set<Type>::value_type;
|
||||
using value_type = Type;
|
||||
using difference_type = typename iterator_impl::difference_type;
|
||||
using pointer = typename flat_multi_set<Type>::pointer;
|
||||
using reference = typename flat_multi_set<Type>::reference;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
flat_multi_set_iterator_base_impl(iterator_impl impl = iterator_impl()) : _impl(impl) {
|
||||
flat_multi_set_iterator_impl(
|
||||
iterator_impl impl = iterator_impl())
|
||||
: _impl(impl) {
|
||||
}
|
||||
template <typename other_iterator_impl>
|
||||
flat_multi_set_iterator_impl(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &other)
|
||||
: _impl(other._impl) {
|
||||
}
|
||||
|
||||
reference operator*() const {
|
||||
@@ -52,188 +62,192 @@ public:
|
||||
pointer operator->() const {
|
||||
return std::addressof(**this);
|
||||
}
|
||||
flat_multi_set_iterator_base_impl &operator++() {
|
||||
flat_multi_set_iterator_impl &operator++() {
|
||||
++_impl;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl operator++(int) {
|
||||
flat_multi_set_iterator_impl operator++(int) {
|
||||
return _impl++;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl &operator--() {
|
||||
flat_multi_set_iterator_impl &operator--() {
|
||||
--_impl;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl operator--(int) {
|
||||
flat_multi_set_iterator_impl operator--(int) {
|
||||
return _impl--;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl &operator+=(difference_type offset) {
|
||||
flat_multi_set_iterator_impl &operator+=(difference_type offset) {
|
||||
_impl += offset;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl operator+(difference_type offset) const {
|
||||
flat_multi_set_iterator_impl operator+(difference_type offset) const {
|
||||
return _impl + offset;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl &operator-=(difference_type offset) {
|
||||
flat_multi_set_iterator_impl &operator-=(difference_type offset) {
|
||||
_impl -= offset;
|
||||
return *this;
|
||||
}
|
||||
flat_multi_set_iterator_base_impl operator-(difference_type offset) const {
|
||||
flat_multi_set_iterator_impl operator-(difference_type offset) const {
|
||||
return _impl - offset;
|
||||
}
|
||||
difference_type operator-(const flat_multi_set_iterator_base_impl &right) const {
|
||||
template <typename other_iterator_impl>
|
||||
difference_type operator-(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl - right._impl;
|
||||
}
|
||||
reference operator[](difference_type offset) const {
|
||||
return _impl[offset];
|
||||
}
|
||||
|
||||
bool operator==(const flat_multi_set_iterator_base_impl &right) const {
|
||||
template <typename other_iterator_impl>
|
||||
bool operator==(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl == right._impl;
|
||||
}
|
||||
bool operator!=(const flat_multi_set_iterator_base_impl &right) const {
|
||||
template <typename other_iterator_impl>
|
||||
bool operator!=(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl != right._impl;
|
||||
}
|
||||
bool operator<(const flat_multi_set_iterator_base_impl &right) const {
|
||||
template <typename other_iterator_impl>
|
||||
bool operator<(
|
||||
const flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
other_iterator_impl> &right) const {
|
||||
return _impl < right._impl;
|
||||
}
|
||||
|
||||
private:
|
||||
iterator_impl _impl;
|
||||
friend class flat_multi_set<Type>;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_multi_set;
|
||||
|
||||
template <typename OtherType, typename OtherCompare>
|
||||
friend class flat_set;
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename other_iterator_impl>
|
||||
friend class flat_multi_set_iterator_impl;
|
||||
|
||||
Type &wrapped() {
|
||||
return _impl->wrapped();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class flat_multi_set_const_wrap {
|
||||
public:
|
||||
constexpr flat_multi_set_const_wrap(const Type &value)
|
||||
: _value(value) {
|
||||
}
|
||||
constexpr flat_multi_set_const_wrap(Type &&value)
|
||||
: _value(std::move(value)) {
|
||||
}
|
||||
inline constexpr operator const Type&() const {
|
||||
return _value;
|
||||
}
|
||||
constexpr Type &wrapped() {
|
||||
return _value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
|
||||
};
|
||||
|
||||
template <typename Type, typename Compare>
|
||||
class flat_multi_set {
|
||||
using self = flat_multi_set<Type>;
|
||||
class const_wrap {
|
||||
public:
|
||||
const_wrap(const Type &value) : _value(value) {
|
||||
}
|
||||
const_wrap(Type &&value) : _value(std::move(value)) {
|
||||
}
|
||||
inline operator const Type&() const {
|
||||
return _value;
|
||||
}
|
||||
|
||||
friend inline bool operator<(const Type &a, const const_wrap &b) {
|
||||
return a < ((const Type&)b);
|
||||
}
|
||||
friend inline bool operator<(const const_wrap &a, const Type &b) {
|
||||
return ((const Type&)a) < b;
|
||||
}
|
||||
friend inline bool operator<(const const_wrap &a, const const_wrap &b) {
|
||||
return ((const Type&)a) < ((const Type&)b);
|
||||
}
|
||||
|
||||
private:
|
||||
Type _value;
|
||||
|
||||
};
|
||||
|
||||
using impl = std::deque<const_wrap>;
|
||||
|
||||
using iterator_base = flat_multi_set_iterator_base_impl<Type, typename impl::iterator>;
|
||||
using const_iterator_base = flat_multi_set_iterator_base_impl<Type, typename impl::const_iterator>;
|
||||
using reverse_iterator_base = flat_multi_set_iterator_base_impl<Type, typename impl::reverse_iterator>;
|
||||
using const_reverse_iterator_base = flat_multi_set_iterator_base_impl<Type, typename impl::const_reverse_iterator>;
|
||||
using const_wrap = flat_multi_set_const_wrap<Type>;
|
||||
using impl_t = std::deque<const_wrap>;
|
||||
|
||||
public:
|
||||
using value_type = Type;
|
||||
using size_type = typename impl::size_type;
|
||||
using difference_type = typename impl::difference_type;
|
||||
using size_type = typename impl_t::size_type;
|
||||
using difference_type = typename impl_t::difference_type;
|
||||
using pointer = const Type*;
|
||||
using reference = const Type&;
|
||||
|
||||
class const_iterator;
|
||||
class iterator : public iterator_base {
|
||||
public:
|
||||
using iterator_base::iterator_base;
|
||||
iterator(const iterator_base &other) : iterator_base(other) {
|
||||
}
|
||||
friend class const_iterator;
|
||||
|
||||
};
|
||||
class const_iterator : public const_iterator_base {
|
||||
public:
|
||||
using const_iterator_base::const_iterator_base;
|
||||
const_iterator(const_iterator_base other) : const_iterator_base(other) {
|
||||
}
|
||||
const_iterator(const iterator &other) : const_iterator_base(other._impl) {
|
||||
}
|
||||
|
||||
};
|
||||
class const_reverse_iterator;
|
||||
class reverse_iterator : public reverse_iterator_base {
|
||||
public:
|
||||
using reverse_iterator_base::reverse_iterator_base;
|
||||
reverse_iterator(reverse_iterator_base other) : reverse_iterator_base(other) {
|
||||
}
|
||||
friend class const_reverse_iterator;
|
||||
|
||||
};
|
||||
class const_reverse_iterator : public const_reverse_iterator_base {
|
||||
public:
|
||||
using const_reverse_iterator_base::const_reverse_iterator_base;
|
||||
const_reverse_iterator(const_reverse_iterator_base other) : const_reverse_iterator_base(other) {
|
||||
}
|
||||
const_reverse_iterator(const reverse_iterator &other) : const_reverse_iterator_base(other._impl) {
|
||||
}
|
||||
|
||||
};
|
||||
using iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::iterator>;
|
||||
using const_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_iterator>;
|
||||
using reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::reverse_iterator>;
|
||||
using const_reverse_iterator = flat_multi_set_iterator_impl<
|
||||
Type,
|
||||
typename impl_t::const_reverse_iterator>;
|
||||
|
||||
flat_multi_set() = default;
|
||||
|
||||
template <typename Iterator, typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
flat_multi_set(Iterator first, Iterator last) : _impl(first, last) {
|
||||
std::sort(_impl.begin(), _impl.end());
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
flat_multi_set(Iterator first, Iterator last)
|
||||
: _data(first, last) {
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
|
||||
flat_multi_set(std::initializer_list<Type> iter)
|
||||
: flat_multi_set(iter.begin(), iter.end()) {
|
||||
}
|
||||
|
||||
size_type size() const {
|
||||
return _impl.size();
|
||||
return impl().size();
|
||||
}
|
||||
bool empty() const {
|
||||
return _impl.empty();
|
||||
return impl().empty();
|
||||
}
|
||||
void clear() {
|
||||
_impl.clear();
|
||||
impl().clear();
|
||||
}
|
||||
|
||||
iterator begin() {
|
||||
return _impl.begin();
|
||||
return impl().begin();
|
||||
}
|
||||
iterator end() {
|
||||
return _impl.end();
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator begin() const {
|
||||
return _impl.begin();
|
||||
return impl().begin();
|
||||
}
|
||||
const_iterator end() const {
|
||||
return _impl.end();
|
||||
return impl().end();
|
||||
}
|
||||
const_iterator cbegin() const {
|
||||
return _impl.cbegin();
|
||||
return impl().cbegin();
|
||||
}
|
||||
const_iterator cend() const {
|
||||
return _impl.cend();
|
||||
return impl().cend();
|
||||
}
|
||||
reverse_iterator rbegin() {
|
||||
return _impl.rbegin();
|
||||
return impl().rbegin();
|
||||
}
|
||||
reverse_iterator rend() {
|
||||
return _impl.rend();
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator rbegin() const {
|
||||
return _impl.rbegin();
|
||||
return impl().rbegin();
|
||||
}
|
||||
const_reverse_iterator rend() const {
|
||||
return _impl.rend();
|
||||
return impl().rend();
|
||||
}
|
||||
const_reverse_iterator crbegin() const {
|
||||
return _impl.crbegin();
|
||||
return impl().crbegin();
|
||||
}
|
||||
const_reverse_iterator crend() const {
|
||||
return _impl.crend();
|
||||
return impl().crend();
|
||||
}
|
||||
|
||||
reference front() const {
|
||||
@@ -244,26 +258,26 @@ public:
|
||||
}
|
||||
|
||||
iterator insert(const Type &value) {
|
||||
if (empty() || (value < front())) {
|
||||
_impl.push_front(value);
|
||||
if (empty() || compare()(value, front())) {
|
||||
impl().push_front(value);
|
||||
return begin();
|
||||
} else if (!(value < back())) {
|
||||
_impl.push_back(value);
|
||||
} else if (!compare()(value, back())) {
|
||||
impl().push_back(value);
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return _impl.insert(where, value);
|
||||
return impl().insert(where, value);
|
||||
}
|
||||
iterator insert(Type &&value) {
|
||||
if (empty() || (value < front())) {
|
||||
_impl.push_front(std::move(value));
|
||||
if (empty() || compare()(value, front())) {
|
||||
impl().push_front(std::move(value));
|
||||
return begin();
|
||||
} else if (!(value < back())) {
|
||||
_impl.push_back(std::move(value));
|
||||
} else if (!compare()(value, back())) {
|
||||
impl().push_back(std::move(value));
|
||||
return (end() - 1);
|
||||
}
|
||||
auto where = getUpperBound(value);
|
||||
return _impl.insert(where, std::move(value));
|
||||
return impl().insert(where, std::move(value));
|
||||
}
|
||||
template <typename... Args>
|
||||
iterator emplace(Args&&... args) {
|
||||
@@ -271,130 +285,354 @@ public:
|
||||
}
|
||||
|
||||
bool removeOne(const Type &value) {
|
||||
if (empty() || (value < front()) || (back() < value)) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return false;
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
if (value < *where) {
|
||||
if (compare()(value, *where)) {
|
||||
return false;
|
||||
}
|
||||
_impl.erase(where);
|
||||
impl().erase(where);
|
||||
return true;
|
||||
}
|
||||
int removeAll(const Type &value) {
|
||||
if (empty() || (value < front()) || (back() < value)) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
if (range.first == range.second) {
|
||||
return 0;
|
||||
}
|
||||
_impl.erase(range.first, range.second);
|
||||
impl().erase(range.first, range.second);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
|
||||
iterator erase(iterator where) {
|
||||
return _impl.erase(where._impl);
|
||||
iterator erase(const_iterator where) {
|
||||
return impl().erase(where._impl);
|
||||
}
|
||||
iterator erase(iterator from, iterator till) {
|
||||
return _impl.erase(from._impl, till._impl);
|
||||
iterator erase(const_iterator from, const_iterator till) {
|
||||
return impl().erase(from._impl, till._impl);
|
||||
}
|
||||
|
||||
iterator findFirst(const Type &value) {
|
||||
if (empty() || (value < front()) || (back() < value)) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return (value < *where) ? _impl.end() : where;
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
const_iterator findFirst(const Type &value) const {
|
||||
if (empty() || (value < front()) || (back() < value)) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return (value < *where) ? _impl.end() : where;
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
iterator findFirst(const OtherType &value) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
const_iterator findFirst(const OtherType &value) const {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return end();
|
||||
}
|
||||
auto where = getLowerBound(value);
|
||||
return compare()(value, *where) ? impl().end() : where;
|
||||
}
|
||||
|
||||
bool contains(const Type &value) const {
|
||||
return findFirst(value) != end();
|
||||
}
|
||||
int count(const Type &value) const {
|
||||
if (empty() || (value < front()) || (back() < value)) {
|
||||
if (empty()
|
||||
|| compare()(value, front())
|
||||
|| compare()(back(), value)) {
|
||||
return 0;
|
||||
}
|
||||
auto range = getEqualRange(value);
|
||||
return (range.second - range.first);
|
||||
}
|
||||
|
||||
private:
|
||||
impl _impl;
|
||||
friend class flat_set<Type>;
|
||||
template <typename Action>
|
||||
auto modify(iterator which, Action action) {
|
||||
auto result = action(which.wrapped());
|
||||
for (auto i = which + 1, e = end(); i != e; ++i) {
|
||||
if (compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
typename impl::iterator getLowerBound(const Type &value) {
|
||||
return std::lower_bound(_impl.begin(), _impl.end(), value);
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
void merge(Iterator first, Iterator last) {
|
||||
impl().insert(impl().end(), first, last);
|
||||
std::sort(std::begin(impl()), std::end(impl()), compare());
|
||||
}
|
||||
typename impl::const_iterator getLowerBound(const Type &value) const {
|
||||
return std::lower_bound(_impl.begin(), _impl.end(), value);
|
||||
|
||||
void merge(const flat_multi_set<Type, Compare> &other) {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
typename impl::iterator getUpperBound(const Type &value) {
|
||||
return std::upper_bound(_impl.begin(), _impl.end(), value);
|
||||
|
||||
void merge(std::initializer_list<Type> list) {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
typename impl::const_iterator getUpperBound(const Type &value) const {
|
||||
return std::upper_bound(_impl.begin(), _impl.end(), value);
|
||||
|
||||
private:
|
||||
friend class flat_set<Type, Compare>;
|
||||
|
||||
struct transparent_compare : Compare {
|
||||
inline constexpr const Compare &initial() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
!std::is_same_v<std::decay_t<OtherType2>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType1>(a),
|
||||
std::forward<OtherType2>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType1,
|
||||
typename OtherType2>
|
||||
inline constexpr auto operator()(
|
||||
OtherType1 &&a,
|
||||
OtherType2 &&b) const -> std::enable_if_t<
|
||||
std::is_same_v<std::decay_t<OtherType1>, const_wrap> &&
|
||||
std::is_same_v<std::decay_t<OtherType2>, const_wrap>, bool> {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
const const_wrap &a,
|
||||
OtherType &&b) const {
|
||||
return initial()(
|
||||
static_cast<const Type&>(a),
|
||||
std::forward<OtherType>(b));
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = std::enable_if_t<
|
||||
!std::is_same_v<std::decay_t<OtherType>, const_wrap>>>
|
||||
inline constexpr auto operator()(
|
||||
OtherType &&a,
|
||||
const const_wrap &b) const {
|
||||
return initial()(
|
||||
std::forward<OtherType>(a),
|
||||
static_cast<const Type&>(b));
|
||||
}
|
||||
|
||||
};
|
||||
struct Data : transparent_compare {
|
||||
template <typename ...Args>
|
||||
Data(Args &&...args)
|
||||
: elements(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
impl_t elements;
|
||||
};
|
||||
|
||||
Data _data;
|
||||
const transparent_compare &compare() const {
|
||||
return _data;
|
||||
}
|
||||
std::pair<typename impl::iterator, typename impl::iterator> getEqualRange(const Type &value) {
|
||||
return std::equal_range(_impl.begin(), _impl.end(), value);
|
||||
const impl_t &impl() const {
|
||||
return _data.elements;
|
||||
}
|
||||
std::pair<typename impl::const_iterator, typename impl::const_iterator> getEqualRange(const Type &value) const {
|
||||
return std::equal_range(_impl.begin(), _impl.end(), value);
|
||||
impl_t &impl() {
|
||||
return _data.elements;
|
||||
}
|
||||
|
||||
typename impl_t::iterator getLowerBound(const Type &value) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::const_iterator getLowerBound(const Type &value) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
typename impl_t::iterator getLowerBound(const OtherType &value) {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
typename impl_t::const_iterator getLowerBound(const OtherType &value) const {
|
||||
return std::lower_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::iterator getUpperBound(const Type &value) {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
typename impl_t::const_iterator getUpperBound(const Type &value) const {
|
||||
return std::upper_bound(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::iterator,
|
||||
typename impl_t::iterator
|
||||
> getEqualRange(const Type &value) {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
std::pair<
|
||||
typename impl_t::const_iterator,
|
||||
typename impl_t::const_iterator
|
||||
> getEqualRange(const Type &value) const {
|
||||
return std::equal_range(
|
||||
std::begin(impl()),
|
||||
std::end(impl()),
|
||||
value,
|
||||
compare());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
class flat_set : public flat_multi_set<Type> {
|
||||
using parent = flat_multi_set<Type>;
|
||||
template <typename Type, typename Compare>
|
||||
class flat_set : private flat_multi_set<Type, Compare> {
|
||||
using parent = flat_multi_set<Type, Compare>;
|
||||
|
||||
public:
|
||||
using parent::parent;
|
||||
using iterator = typename parent::iterator;
|
||||
using const_iterator = typename parent::const_iterator;
|
||||
using reverse_iterator = typename parent::reverse_iterator;
|
||||
using const_reverse_iterator = typename parent::const_reverse_iterator;
|
||||
using value_type = typename parent::value_type;
|
||||
using size_type = typename parent::size_type;
|
||||
using difference_type = typename parent::difference_type;
|
||||
using pointer = typename parent::pointer;
|
||||
using reference = typename parent::reference;
|
||||
|
||||
flat_set() = default;
|
||||
|
||||
template <typename Iterator, typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category
|
||||
>
|
||||
flat_set(Iterator first, Iterator last) : parent(first, last) {
|
||||
this->_impl.erase(std::unique(this->_impl.begin(), this->_impl.end(), [](auto &&a, auto &&b) {
|
||||
return !(a < b);
|
||||
}), this->_impl.end());
|
||||
finalize();
|
||||
}
|
||||
|
||||
flat_set(std::initializer_list<Type> iter) : parent(iter.begin(), iter.end()) {
|
||||
finalize();
|
||||
}
|
||||
|
||||
using parent::parent;
|
||||
using parent::size;
|
||||
using parent::empty;
|
||||
using parent::clear;
|
||||
using parent::begin;
|
||||
using parent::end;
|
||||
using parent::cbegin;
|
||||
using parent::cend;
|
||||
using parent::rbegin;
|
||||
using parent::rend;
|
||||
using parent::crbegin;
|
||||
using parent::crend;
|
||||
using parent::front;
|
||||
using parent::back;
|
||||
using parent::contains;
|
||||
using parent::erase;
|
||||
|
||||
iterator insert(const Type &value) {
|
||||
if (this->empty() || (value < this->front())) {
|
||||
this->_impl.push_front(value);
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(value);
|
||||
return this->begin();
|
||||
} else if (this->back() < value) {
|
||||
this->_impl.push_back(value);
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(value);
|
||||
return (this->end() - 1);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (value < *where) {
|
||||
return this->_impl.insert(where, value);
|
||||
if (this->compare()(value, *where)) {
|
||||
return this->impl().insert(where, value);
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
iterator insert(Type &&value) {
|
||||
if (this->empty() || (value < this->front())) {
|
||||
this->_impl.push_front(std::move(value));
|
||||
if (this->empty() || this->compare()(value, this->front())) {
|
||||
this->impl().push_front(std::move(value));
|
||||
return this->begin();
|
||||
} else if (this->back() < value) {
|
||||
this->_impl.push_back(std::move(value));
|
||||
} else if (this->compare()(this->back(), value)) {
|
||||
this->impl().push_back(std::move(value));
|
||||
return (this->end() - 1);
|
||||
}
|
||||
auto where = this->getLowerBound(value);
|
||||
if (value < *where) {
|
||||
return this->_impl.insert(where, std::move(value));
|
||||
if (this->compare()(value, *where)) {
|
||||
return this->impl().insert(where, std::move(value));
|
||||
}
|
||||
return this->end();
|
||||
}
|
||||
@@ -413,6 +651,73 @@ public:
|
||||
const_iterator find(const Type &value) const {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
iterator find(const OtherType &value) {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
template <
|
||||
typename OtherType,
|
||||
typename = typename Compare::is_transparent>
|
||||
const_iterator find(const OtherType &value) const {
|
||||
return this->findFirst(value);
|
||||
}
|
||||
|
||||
template <typename Action>
|
||||
void modify(iterator which, Action action) {
|
||||
action(which.wrapped());
|
||||
for (auto i = iterator(which + 1), e = end(); i != e; ++i) {
|
||||
if (this->compare()(*i, *which)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*which, *i)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto i = which, b = begin(); i != b;) {
|
||||
--i;
|
||||
if (this->compare()(*which, *i)) {
|
||||
std::swap(i.wrapped(), which.wrapped());
|
||||
} else if (!this->compare()(*i, *which)) {
|
||||
erase(which);
|
||||
return;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <
|
||||
typename Iterator,
|
||||
typename = typename std::iterator_traits<Iterator>::iterator_category>
|
||||
void merge(Iterator first, Iterator last) {
|
||||
parent::merge(first, last);
|
||||
finalize();
|
||||
}
|
||||
|
||||
void merge(const flat_multi_set<Type, Compare> &other) {
|
||||
merge(other.begin(), other.end());
|
||||
}
|
||||
|
||||
void merge(std::initializer_list<Type> list) {
|
||||
merge(list.begin(), list.end());
|
||||
}
|
||||
|
||||
private:
|
||||
void finalize() {
|
||||
this->impl().erase(
|
||||
std::unique(
|
||||
std::begin(this->impl()),
|
||||
std::end(this->impl()),
|
||||
[&](auto &&a, auto &&b) {
|
||||
return !this->compare()(a, b);
|
||||
}
|
||||
),
|
||||
std::end(this->impl()));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -22,13 +22,35 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
#include "base/flat_set.h"
|
||||
|
||||
struct int_wrap {
|
||||
int value;
|
||||
};
|
||||
struct int_wrap_comparator {
|
||||
using is_transparent = void;
|
||||
inline bool operator()(const int &a, const int_wrap &b) const {
|
||||
return a < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int_wrap &b) const {
|
||||
return a.value < b.value;
|
||||
}
|
||||
inline bool operator()(const int_wrap &a, const int &b) const {
|
||||
return a.value < b;
|
||||
}
|
||||
inline bool operator()(const int &a, const int &b) const {
|
||||
return a < b;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_CASE("flat_sets should keep items sorted", "[flat_set]") {
|
||||
|
||||
base::flat_set<int> v;
|
||||
v.insert(0);
|
||||
v.insert(5);
|
||||
v.insert(4);
|
||||
v.insert(2);
|
||||
|
||||
REQUIRE(v.contains(4));
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
@@ -46,3 +68,30 @@ TEST_CASE("flat_sets should keep items sorted", "[flat_set]") {
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("flat_sets with custom comparators", "[flat_set]") {
|
||||
base::flat_set<int_wrap, int_wrap_comparator> v;
|
||||
v.insert({ 0 });
|
||||
v.insert({ 5 });
|
||||
v.insert({ 4 });
|
||||
v.insert({ 2 });
|
||||
|
||||
REQUIRE(v.find(4) != v.end());
|
||||
|
||||
auto checkSorted = [&] {
|
||||
auto prev = v.begin();
|
||||
REQUIRE(prev != v.end());
|
||||
for (auto i = prev + 1; i != v.end(); prev = i, ++i) {
|
||||
REQUIRE(prev->value < i->value);
|
||||
}
|
||||
};
|
||||
REQUIRE(v.size() == 4);
|
||||
checkSorted();
|
||||
|
||||
SECTION("adding item puts it in the right position") {
|
||||
v.insert({ 3 });
|
||||
REQUIRE(v.size() == 5);
|
||||
REQUIRE(v.find(3) != v.end());
|
||||
checkSorted();
|
||||
}
|
||||
}
|
||||
|
||||