diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/no-response.yml b/.github/workflows/cant-reproduce.yml similarity index 57% rename from .github/workflows/no-response.yml rename to .github/workflows/cant-reproduce.yml index f414a55fd..5c4edcba0 100644 --- a/.github/workflows/no-response.yml +++ b/.github/workflows/cant-reproduce.yml @@ -1,32 +1,11 @@ -name: No Response +name: Can't reproduce. -# Both `issue_comment` and `scheduled` event types are required for this Action -# to work properly. on: - issue_comment: - types: [created] schedule: - - cron: '0 0 * * *' + - cron: '0 3 * * *' jobs: - waiting-for-answer: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: waiting for answer - - needs-user-action: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} - responseRequiredLabel: needs user action - cant-reproduce: - if: github.event_name != 'issue_comment' runs-on: ubuntu-latest steps: - uses: lee-dohm/no-response@v0.5.0 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 7a65b483f..ac40f3a33 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive @@ -31,7 +31,7 @@ jobs: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin - name: Free up some disk space. - uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - name: Docker image build. run: | diff --git a/.github/workflows/linux-kotato.yml b/.github/workflows/linux-kotato.yml deleted file mode 100644 index d1349fc9f..000000000 --- a/.github/workflows/linux-kotato.yml +++ /dev/null @@ -1,534 +0,0 @@ -name: Linux Kotato. - -on: - push: - paths-ignore: - - 'docs/**' - - '**.md' - - '!docs/building-cmake.md' - - 'kotatogram_changes.txt' - - 'changelog.txt' - - 'LEGAL' - - 'LICENSE' - - '.github/**' - - '!.github/workflows/linux-kotato.yml' - - 'snap/**' - - 'Telegram/build/**' - - 'Telegram/Patches/**' - - 'Telegram/Resources/uwp/**' - - 'Telegram/Resources/winrc/**' - - 'Telegram/SourceFiles/platform/win/**' - - 'Telegram/SourceFiles/platform/mac/**' - - 'Telegram/Telegram/**' - - 'Telegram/configure.bat' - - 'Telegram/Telegram.plist' - pull_request: - paths-ignore: - - 'docs/**' - - '**.md' - - '!docs/building-cmake.md' - - 'kotatogram_changes.txt' - - 'changelog.txt' - - 'LEGAL' - - 'LICENSE' - - '.github/**' - - '!.github/workflows/linux-kotato.yml' - - 'snap/**' - - 'Telegram/build/**' - - 'Telegram/Patches/**' - - 'Telegram/Resources/uwp/**' - - 'Telegram/Resources/winrc/**' - - 'Telegram/SourceFiles/platform/win/**' - - 'Telegram/SourceFiles/platform/mac/**' - - 'Telegram/Telegram/**' - - 'Telegram/configure.bat' - - 'Telegram/Telegram.plist' - -jobs: - - linux: - name: Ubuntu 18.04 - if: > - !(github.event_name == 'push' - && contains(github.event.head_commit.message, '[skip ci]')) - && !(github.event_name == 'pull_request' - && github.event.pull_request.head.repo.full_name == github.event.pull_request.base.repo.full_name) - runs-on: ubuntu-latest - container: - image: ubuntu:bionic - options: --privileged --device /dev/fuse - - strategy: - matrix: - defines: - - "" - - "DESKTOP_APP_DISABLE_DBUS_INTEGRATION" - - "DESKTOP_APP_DISABLE_X11_INTEGRATION" - - "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" - - env: - GIT: "https://github.com" - QT: "6_2_3" - CMAKE_VER: "3.21.3" - UPLOAD_ARTIFACT: "true" - ONLY_CACHE: "false" - MANUAL_CACHING: "1" - AUTO_CACHING: "1" - CXX: "g++ -static-libstdc++" - - steps: - - name: Get repository name. - run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Disable man for further package installs. - run: | - cfgFile="/etc/dpkg/dpkg.cfg.d/no_man" - touch $cfgFile - p() { - echo "path-exclude=/usr/share/$1/*" >> $cfgFile - } - - p man - p locale - p doc - - - name: Apt install. - run: | - apt-get update - apt-get install software-properties-common -y && \ - add-apt-repository ppa:git-core/ppa -y && \ - apt-get update && \ - apt-get install clang libglibmm-2.4-dev libicu-dev libssl-dev liblzma-dev zlib1g-dev \ - git wget autoconf automake build-essential libtool pkg-config bison yasm unzip python3-pip \ - libasound2-dev libpulse-dev libfuse2 libgtk-3-dev libgtk2.0-dev libatspi2.0-dev \ - libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev libxkbcommon-dev libxkbcommon-x11-dev \ - libxcb1-dev libxcb-glx0-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev \ - libxcb-randr0-dev libxcb-record0-dev libxcb-render0-dev libxcb-render-util0-dev \ - libxcb-res0-dev libxcb-screensaver0-dev libxcb-shape0-dev libxcb-shm0-dev \ - libxcb-sync-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-xkb-dev \ - libxcb-util0-dev libx11-dev libx11-xcb-dev libxext-dev libxtst-dev libxfixes-dev \ - libxrandr-dev libxrender-dev libxdamage-dev libxcomposite-dev libwayland-dev \ - xutils-dev meson ninja-build subversion -y --force-yes && \ - add-apt-repository ppa:ubuntu-toolchain-r/test -y && \ - apt-get update && \ - apt-get install gcc-10 g++-10 -y && \ - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-10 60 && \ - update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 60 && \ - update-alternatives --config gcc && \ - add-apt-repository --remove ppa:ubuntu-toolchain-r/test -y - - - name: Clone. - uses: actions/checkout@v2 - with: - submodules: recursive - path: ${{ env.REPO_NAME }} - - - name: First set up. - run: | - wget -c -nv "$GIT/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" - chmod a+x linuxdeployqt-continuous-x86_64.AppImage - - ./linuxdeployqt-continuous-x86_64.AppImage -version - gcc --version - - gcc --version > CACHE_KEY.txt - echo $MANUAL_CACHING >> CACHE_KEY.txt - if [ "$AUTO_CACHING" = "1" ]; then - thisFile=$REPO_NAME/.github/workflows/linux-kotato.yml - echo `md5sum $thisFile | cut -c -32` >> CACHE_KEY.txt - fi - md5cache=$(md5sum CACHE_KEY.txt | cut -c -32) - echo "CACHE_KEY=$md5cache" >> $GITHUB_ENV - - mkdir -p Libraries - cd Libraries - echo "LibrariesPath=`pwd`" >> $GITHUB_ENV - - wget -O tg_owt-version.json https://api.github.com/repos/desktop-app/tg_owt/git/refs/heads/master - - - name: Patches. - run: | - cd $LibrariesPath - git clone --depth=1 $GIT/desktop-app/patches.git - - - name: Rnnoise. - run: | - cd $LibrariesPath - - git clone --depth=1 https://gitlab.xiph.org/xiph/rnnoise.git - cd rnnoise - ./autogen.sh - ./configure --disable-examples --disable-doc - make -j$(nproc) - make install - - - name: CMake. - run: | - cd $LibrariesPath - - file=cmake-$CMAKE_VER-Linux-x86_64.sh - wget $GIT/Kitware/CMake/releases/download/v$CMAKE_VER/$file - mkdir /opt/cmake - sh $file --prefix=/opt/cmake --skip-license - ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake - rm $file - - cmake --version - - - name: Meson. - run: | - python3 -m pip install meson==0.54.0 - meson --version - - - name: Ninja. - run: | - cd $LibrariesPath - - wget $GIT/ninja-build/ninja/releases/download/v1.10.0/ninja-linux.zip - unzip ninja-linux.zip - chmod +x ninja - mv ninja /usr/local/bin - - ninja --version - - - name: MozJPEG. - run: | - cd $LibrariesPath - - git clone -b v4.0.3 --depth=1 $GIT/mozilla/mozjpeg.git - cd mozjpeg - cmake -B build -GNinja . \ - -DCMAKE_BUILD_TYPE=Release \ - -DCMAKE_INSTALL_PREFIX=/usr/local \ - -DWITH_JPEG8=ON \ - -DPNG_SUPPORTED=OFF - cmake --build build --parallel - cmake --install build - cd .. - rm -rf mozjpeg - - - name: Opus cache. - id: cache-opus - uses: actions/cache@v2 - with: - path: ${{ env.LibrariesPath }}/opus - key: ${{ runner.OS }}-opus-${{ env.CACHE_KEY }} - - name: Opus. - if: steps.cache-opus.outputs.cache-hit != 'true' - run: | - cd $LibrariesPath - - git clone -b v1.3.1 --depth=1 $GIT/xiph/opus - cd opus - ./autogen.sh - ./configure - make -j$(nproc) - - name: Opus install. - run: | - cd $LibrariesPath/opus - make install - - - name: VPX cache. - id: cache-vpx - uses: actions/cache@v2 - with: - path: ${{ env.LibrariesPath }}/vpx-cache - key: ${{ runner.OS }}-vpx-${{ env.CACHE_KEY }} - - name: VPX build. - if: steps.cache-vpx.outputs.cache-hit != 'true' - run: | - cd $LibrariesPath - - git clone -b v1.11.0 --depth=1 $GIT/webmproject/libvpx.git - cd libvpx - ./configure \ - --prefix=/usr \ - --disable-examples \ - --disable-unit-tests \ - --disable-tools \ - --disable-docs \ - --enable-shared \ - --disable-static \ - --enable-vp8 \ - --enable-vp9 \ - --enable-webm-io - - make -j$(nproc) - make DESTDIR="$LibrariesPath/vpx-cache" install - cd .. - rm -rf libvpx - - name: VPX install. - run: | - cd $LibrariesPath - cp -R vpx-cache/. / - ldconfig - - - name: FFmpeg cache. - id: cache-ffmpeg - uses: actions/cache@v2 - with: - path: ${{ env.LibrariesPath }}/ffmpeg-cache - key: ${{ runner.OS }}-ffmpeg-${{ env.CACHE_KEY }} - - name: FFmpeg build. - if: steps.cache-ffmpeg.outputs.cache-hit != 'true' - run: | - cd $LibrariesPath - - git clone --branch release/4.4 --depth=1 $GIT/FFmpeg/FFmpeg ffmpeg - cd ffmpeg - ./configure \ - --disable-static \ - --disable-debug \ - --disable-programs \ - --disable-doc \ - --disable-network \ - --disable-autodetect \ - --disable-encoders \ - --disable-muxers \ - --disable-bsfs \ - --disable-protocols \ - --disable-devices \ - --disable-filters \ - --enable-shared \ - --enable-libopus \ - --enable-libvpx \ - --enable-protocol=file \ - --enable-encoder=libopus \ - --enable-muxer=ogg \ - --enable-muxer=opus - - make -j$(nproc) - make DESTDIR="$LibrariesPath/ffmpeg-cache" install - cd .. - rm -rf ffmpeg - - name: FFmpeg install. - run: | - cd $LibrariesPath - cp -R ffmpeg-cache/. / - ldconfig - - - name: OpenAL Soft. - run: | - cd $LibrariesPath - - git clone -b fix_pulse_default --depth=1 $GIT/telegramdesktop/openal-soft.git - cd openal-soft - cmake -B build -GNinja \ - -DCMAKE_BUILD_TYPE=Release \ - -DALSOFT_EXAMPLES=OFF \ - -DALSOFT_TESTS=OFF \ - -DALSOFT_UTILS=OFF \ - -DALSOFT_CONFIG=OFF - - cmake --build build --parallel - cmake --install build - ldconfig - - cd - - rm -rf openal-soft - - - name: Libepoxy. - run: | - cd $LibrariesPath - - git clone -b 1.5.9 --depth=1 $GIT/anholt/libepoxy.git - - cd libepoxy - git apply ../patches/libepoxy.patch - - meson build \ - --buildtype=release \ - --default-library=static \ - -Dtests=false - - meson compile -C build - meson install -C build - cd .. - rm -rf libepoxy - - - name: Qt 6.2.3 cache. - id: cache-qt - uses: actions/cache@v2 - with: - path: ${{ env.LibrariesPath }}/qt-cache - key: ${{ runner.OS }}-qt-${{ env.CACHE_KEY }}-${{ hashFiles('**/qtbase_6_2_3/*') }}-${{ hashFiles('**/qtwayland_6_2_3/*') }}-${{ hashFiles('**/qt5compat_6_2_3/*') }} - - name: Qt 6.2.3 build. - if: steps.cache-qt.outputs.cache-hit != 'true' - run: | - cd $LibrariesPath - - git clone -b v6.2.3 --depth=1 git://code.qt.io/qt/qt5.git qt_${QT} - cd qt_${QT} - perl init-repository --module-subset=qtbase,qtwayland,qtimageformats,qtsvg,qt5compat,qttools - cd qtbase - find ../../patches/qtbase_${QT} -type f -print0 | sort -z | xargs -r0 git apply - cd ../qtwayland - find ../../patches/qtwayland_${QT} -type f -print0 | sort -z | xargs -r0 git apply - cd ../qt5compat - find ../../patches/qt5compat_${QT} -type f -print0 | sort -z | xargs -r0 git apply - cd .. - - ./configure -prefix /usr/local \ - -release \ - -opensource \ - -confirm-license \ - -qt-libpng \ - -qt-harfbuzz \ - -qt-pcre \ - -no-feature-xcb-sm \ - -no-feature-highdpiscaling \ - -openssl-linked \ - -nomake examples \ - -nomake tests - - cmake --build . --parallel - DESTDIR="$LibrariesPath/qt-cache" cmake --install . - cd .. - rm -rf qt_${QT} - - name: Qt 6.2.3 install. - run: | - cd $LibrariesPath - cp -R qt-cache/. / - ldconfig - - - name: Qt6Gtk2. - run: | - cd $LibrariesPath - - git clone -b 0.1 --depth=1 $GIT/trialuser02/qt6gtk2.git - cd qt6gtk2 - wget https://github.com/trialuser02/qt6gtk2/commit/3d2cf8cbade92a175b2c878090f5f44a1b8a395c.patch - git apply 3d2cf8cbade92a175b2c878090f5f44a1b8a395c.patch - qmake - make -j$(nproc) - make install - cd .. - rm -rf qt6gtk2 - - - name: Qt6Ct. - run: | - cd $LibrariesPath - - git clone -b 0.5 --depth=1 $GIT/trialuser02/qt6ct.git - cd qt6ct - cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release - cmake --build build --parallel - cmake --install build - cd .. - rm -rf qt6ct - - - name: Kvantum. - run: | - cd $LibrariesPath - - git clone -b V1.0.0 --depth=1 $GIT/tsujan/Kvantum.git - cd Kvantum/Kvantum - cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release -DENABLE_QT5=OFF - cmake --build build --parallel - cmake --install build - cd ../.. - rm -rf Kvantum - - - name: WebRTC cache. - id: cache-webrtc - uses: actions/cache@v2 - with: - path: ${{ env.LibrariesPath }}/tg_owt - key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }} - - name: WebRTC. - if: steps.cache-webrtc.outputs.cache-hit != 'true' - run: | - cd $LibrariesPath - - mkdir tg_owt - cd tg_owt - git init - git remote add origin $GIT/desktop-app/tg_owt.git - git fetch --depth=1 origin 4cba1acdd718b700bb33945c0258283689d4eac7 - git reset --hard FETCH_HEAD - git submodule init - git submodule update - - cd src/third_party/pipewire - meson build -Dspa-plugins=disabled - cd ../../.. - - cmake -B build -GNinja . \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_SHARED_LIBS=OFF \ - -DTG_OWT_DLOPEN_PIPEWIRE=ON - - cmake --build build --parallel - - - name: Kotatogram Desktop build. - if: env.ONLY_CACHE == 'false' - env: - tg_owt_DIR: ${{ env.LibrariesPath }}/tg_owt/build - run: | - cd $REPO_NAME/Telegram - - DEFINE="" - if [ -n "${{ matrix.defines }}" ]; then - DEFINE="-D ${{ matrix.defines }}=ON" - echo Define from matrix: $DEFINE - echo "ARTIFACT_NAME=Kotatogram_${{ matrix.defines }}" >> $GITHUB_ENV - else - echo "ARTIFACT_NAME=Kotatogram" >> $GITHUB_ENV - fi - - ./configure.sh \ - -DCMAKE_INSTALL_PREFIX=/usr \ - -DCMAKE_C_FLAGS="-Werror" \ - -DCMAKE_CXX_FLAGS="-Werror" \ - -DCMAKE_EXE_LINKER_FLAGS="-s" \ - -DTDESKTOP_API_TEST=ON \ - -DDESKTOP_APP_USE_PACKAGED_LAZY=ON \ - $DEFINE - - cmake --build ../out --config Debug --parallel - DESTDIR=../AppDir cmake --install ../out --config Debug - - - name: AppImage build. - if: env.ONLY_CACHE == 'false' - run: | - # Let appimagetool determine the repository - cd $REPO_NAME - - ../linuxdeployqt-continuous-x86_64.AppImage \ - AppDir/usr/share/applications/*.desktop \ - -appimage \ - -exclude-libs=libatk-1.0.so.0,libatk-bridge-2.0.so.0,libatspi.so.0,libcairo-gobject.so.2,libcairo.so.2,libgdk-3.so.0,libgdk_pixbuf-2.0.so.0,libgdk-x11-2.0.so.0,libgmodule-2.0.so.0,libgtk-3.so.0,libgtk-x11-2.0.so.0,libpixman-1.so.0,libpng12.so.0 \ - -extra-plugins=iconengines,imageformats,platforminputcontexts,platforms/libqwayland-egl.so,platforms/libqwayland-generic.so,platforms/libqxcb.so,platformthemes/libqgtk3.so,platformthemes/libqxdgdesktopportal.so,platformthemes/libqt6ct.so,platformthemes/libqt6gtk2.so,styles/libqt6ct-style.so,styles/libqt6gtk2-style.so,styles/libkvantum.so,tls,wayland-decoration-client,wayland-graphics-integration-client,wayland-shell-integration,xcbglintegrations - - appimage_name=$(echo Kotatogram_Desktop*.AppImage) - echo "APPIMAGE_NAME=$appimage_name" >> $GITHUB_ENV - - # We don't need AppImageLauncher's desktop integration due to bad UX - dd if=/dev/zero of=$appimage_name bs=1 count=3 seek=8 conv=notrunc - - - name: Check. - if: env.ONLY_CACHE == 'false' - run: | - filePath="$REPO_NAME/$APPIMAGE_NAME" - if test -f "$filePath"; then - echo "Build successfully done! :)" - - size=$(stat -c %s "$filePath") - echo "File size of ${filePath}: ${size} Bytes." - else - echo "Build error, output file does not exist." - exit 1 - fi - - - name: Move artifact. - if: env.UPLOAD_ARTIFACT == 'true' - run: | - cd $REPO_NAME - mkdir artifact - mv $APPIMAGE_NAME artifact/ - - uses: actions/upload-artifact@master - if: env.UPLOAD_ARTIFACT == 'true' - name: Upload artifact. - with: - name: ${{ env.ARTIFACT_NAME }} - path: ${{ env.REPO_NAME }}/artifact/ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 705d6538a..a27ce967f 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -55,18 +55,17 @@ jobs: matrix: defines: - "" - #- "DESKTOP_APP_DISABLE_X11_INTEGRATION" - #- "DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION" + - "DESKTOP_APP_DISABLE_X11_INTEGRATION" env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" steps: - name: Get repository name. run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -122,9 +121,9 @@ jobs: if: env.UPLOAD_ARTIFACT == 'true' run: | cd $REPO_NAME/out/Debug - mkdir artifact - mv {Kotatogram,Updater} artifact/ - - uses: actions/upload-artifact@master + sudo mkdir artifact + sudo mv {Kotatogram,Updater} artifact/ + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index 562159d0d..cf3c46989 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -8,7 +8,7 @@ jobs: lock: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v3 + - uses: dessant/lock-threads@v5 with: github-token: ${{ github.token }} issue-inactive-days: 45 diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 94d9b8c6a..f453bc9ba 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -54,7 +54,7 @@ jobs: defines: - "" env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" PREPARE_PATH: "Telegram/build/prepare/prepare.py" @@ -63,7 +63,7 @@ jobs: run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -71,7 +71,10 @@ jobs: - name: First set up. run: | sudo chown -R `whoami`:admin /usr/local/share - brew install automake ninja pkg-config nasm meson + + brew update + brew upgrade || true + brew install automake meson nasm ninja pkg-config # Disable spotlight. sudo mdutil -a -i off @@ -80,7 +83,7 @@ jobs: - name: ThirdParty cache. id: cache-third-party - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ThirdParty key: ${{ runner.OS }}-third-party-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }} @@ -88,7 +91,7 @@ jobs: - name: Libraries cache. id: cache-libs - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: Libraries key: ${{ runner.OS }}-libs-${{ hashFiles(format('{0}/{1}', env.REPO_NAME, env.PREPARE_PATH)) }} @@ -138,7 +141,7 @@ jobs: mkdir artifact mv Kotatogram.app artifact/ mv Updater artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/mac_packaged.yml b/.github/workflows/mac_packaged.yml index 23187ffcc..e9f7f2377 100644 --- a/.github/workflows/mac_packaged.yml +++ b/.github/workflows/mac_packaged.yml @@ -49,8 +49,8 @@ jobs: env: GIT: "https://github.com" - OPENALDIR: "/usr/local/opt/openal-soft" - UPLOAD_ARTIFACT: "false" + CMAKE_PREFIX_PATH: "/usr/local/opt/ffmpeg@6:/usr/local/opt/openal-soft" + UPLOAD_ARTIFACT: "true" ONLY_CACHE: "false" MANUAL_CACHING: "1" AUTO_CACHING: "1" @@ -60,7 +60,7 @@ jobs: run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.REPO_NAME }} @@ -69,7 +69,7 @@ jobs: run: | brew update brew upgrade || true - brew install autoconf automake boost cmake ffmpeg openal-soft openssl opus ninja pkg-config python qt yasm xz + brew install autoconf automake boost cmake ffmpeg@6 openal-soft openssl opus ninja pkg-config python qt yasm xz sudo xcode-select -s /Applications/Xcode.app/Contents/Developer xcodebuild -version > CACHE_KEY.txt @@ -99,7 +99,7 @@ jobs: - name: WebRTC cache. id: cache-webrtc - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ${{ env.LibrariesPath }}/tg_owt key: ${{ runner.OS }}-webrtc-${{ env.CACHE_KEY }}-${{ hashFiles('**/tg_owt-version.json') }} @@ -153,7 +153,7 @@ jobs: cd $REPO_NAME/build mkdir artifact mv Kotatogram.dmg artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/master_updater.yml b/.github/workflows/master_updater.yml index 284bb844e..c59f62e29 100644 --- a/.github/workflows/master_updater.yml +++ b/.github/workflows/master_updater.yml @@ -11,7 +11,7 @@ jobs: SKIP: "0" to_branch: "master" steps: - - uses: actions/checkout@v4.1.0 + - uses: actions/checkout@v4 with: fetch-depth: 0 if: env.SKIP == '0' diff --git a/.github/workflows/needs-user-action.yml b/.github/workflows/needs-user-action.yml new file mode 100644 index 000000000..46ad9f87d --- /dev/null +++ b/.github/workflows/needs-user-action.yml @@ -0,0 +1,16 @@ +name: Needs user action. + +on: + issue_comment: + types: [created] + schedule: + - cron: '0 2 * * *' + +jobs: + needs-user-action: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: needs user action diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml index c2e5dbafe..93ef6ad36 100644 --- a/.github/workflows/snap.yml +++ b/.github/workflows/snap.yml @@ -43,11 +43,11 @@ jobs: runs-on: ubuntu-20.04 env: - UPLOAD_ARTIFACT: "false" + UPLOAD_ARTIFACT: "true" steps: - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive @@ -61,10 +61,10 @@ jobs: sudo snap run lxd waitready - name: Free up some disk space. - uses: jlumbroso/free-disk-space@f68fdb76e2ea636224182cfb7377ff9a1708f9b8 + uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be - name: Telegram Desktop snap build. - run: sg lxd -c 'snap run snapcraft -v' + run: sg lxd -c 'snap run snapcraft --verbosity=debug' - name: Move artifact. if: env.UPLOAD_ARTIFACT == 'true' @@ -75,7 +75,7 @@ jobs: mkdir artifact mv $artifact_name artifact - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 if: env.UPLOAD_ARTIFACT == 'true' name: Upload artifact. with: diff --git a/.github/workflows/waiting-for-answer.yml b/.github/workflows/waiting-for-answer.yml new file mode 100644 index 000000000..5e5ff87c9 --- /dev/null +++ b/.github/workflows/waiting-for-answer.yml @@ -0,0 +1,16 @@ +name: Waiting for answer. + +on: + issue_comment: + types: [created] + schedule: + - cron: '30 0 * * *' + +jobs: + waiting-for-answer: + runs-on: ubuntu-latest + steps: + - uses: lee-dohm/no-response@v0.5.0 + with: + token: ${{ github.token }} + responseRequiredLabel: waiting for answer diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index 2c60ada70..552b0ef16 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -76,13 +76,13 @@ jobs: shell: bash run: echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - uses: ilammy/msvc-dev-cmd@v1.12.0 + - uses: ilammy/msvc-dev-cmd@v1.13.0 name: Native Tools Command Prompt. with: arch: ${{ matrix.arch }} - name: Clone. - uses: actions/checkout@v3.1.0 + uses: actions/checkout@v4 with: submodules: recursive path: ${{ env.TBUILD }}\${{ env.REPO_NAME }} @@ -103,7 +103,7 @@ jobs: - name: Libraries cache. id: cache-libs - uses: actions/cache@v3.0.11 + uses: actions/cache@v4 with: path: ${{ env.TBUILD }}\Libraries key: ${{ runner.OS }}-${{ matrix.arch }}-libs-${{ env.CACHE_KEY }} @@ -199,7 +199,7 @@ jobs: mkdir artifact move %OUT%\Kotatogram.exe artifact/ move %OUT%\Updater.exe artifact/ - - uses: actions/upload-artifact@master + - uses: actions/upload-artifact@v4 name: Upload artifact. if: (env.UPLOAD_ARTIFACT == 'true') || (github.ref == 'refs/heads/nightly') with: diff --git a/.gitignore b/.gitignore index 3897af2b3..56d26f58d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ Release/ *.xcodeproj ipch/ .vs/ +.vscode/ /Telegram/log.txt /Telegram/data diff --git a/.gitmodules b/.gitmodules index 07f7ed400..8b8d8d243 100644 --- a/.gitmodules +++ b/.gitmodules @@ -76,15 +76,12 @@ [submodule "Telegram/lib_webview"] path = Telegram/lib_webview url = https://github.com/desktop-app/lib_webview.git +[submodule "Telegram/ThirdParty/jemalloc"] + path = Telegram/ThirdParty/jemalloc + url = https://github.com/jemalloc/jemalloc [submodule "Telegram/ThirdParty/dispatch"] path = Telegram/ThirdParty/dispatch url = https://github.com/apple/swift-corelibs-libdispatch -[submodule "Telegram/ThirdParty/plasma-wayland-protocols"] - path = Telegram/ThirdParty/plasma-wayland-protocols - url = https://github.com/KDE/plasma-wayland-protocols.git -[submodule "Telegram/ThirdParty/wayland-protocols"] - path = Telegram/ThirdParty/wayland-protocols - url = https://github.com/gitlab-freedesktop-mirrors/wayland-protocols.git [submodule "Telegram/ThirdParty/kimageformats"] path = Telegram/ThirdParty/kimageformats url = https://github.com/KDE/kimageformats.git @@ -94,9 +91,6 @@ [submodule "Telegram/ThirdParty/cld3"] path = Telegram/ThirdParty/cld3 url = https://github.com/google/cld3.git -[submodule "Telegram/ThirdParty/wayland"] - path = Telegram/ThirdParty/wayland - url = https://github.com/gitlab-freedesktop-mirrors/wayland.git [submodule "Telegram/ThirdParty/libprisma"] path = Telegram/ThirdParty/libprisma url = https://github.com/desktop-app/libprisma.git diff --git a/CMakeLists.txt b/CMakeLists.txt index dcf88602f..4e2ac34c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,12 +62,13 @@ if (NOT DESKTOP_APP_USE_PACKAGED) if (WIN32) set(qt_version 5.15.13) elseif (APPLE) - set(qt_version 6.2.7) + set(qt_version 6.2.8) endif() endif() include(cmake/external/qt/package.cmake) set(desktop_app_skip_libs + glibmm variant ) diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 330190fdf..884778919 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -110,6 +110,8 @@ PRIVATE api/api_chat_filters.h api/api_chat_invite.cpp api/api_chat_invite.h + api/api_chat_links.cpp + api/api_chat_links.h api/api_chat_participants.cpp api/api_chat_participants.h api/api_cloud_password.cpp @@ -118,6 +120,8 @@ PRIVATE api/api_common.h api/api_confirm_phone.cpp api/api_confirm_phone.h + api/api_earn.cpp + api/api_earn.h api/api_editing.cpp api/api_editing.h api/api_global_privacy.cpp @@ -269,6 +273,8 @@ PRIVATE boxes/local_storage_box.h boxes/max_invite_box.cpp boxes/max_invite_box.h + boxes/moderate_messages_box.cpp + boxes/moderate_messages_box.h boxes/peer_list_box.cpp boxes/peer_list_box.h boxes/peer_list_controllers.cpp @@ -392,8 +398,6 @@ PRIVATE chat_helpers/spellchecker_common.h chat_helpers/stickers_dice_pack.cpp chat_helpers/stickers_dice_pack.h - chat_helpers/stickers_emoji_image_loader.cpp - chat_helpers/stickers_emoji_image_loader.h chat_helpers/stickers_emoji_pack.cpp chat_helpers/stickers_emoji_pack.h chat_helpers/stickers_gift_box_pack.cpp @@ -458,6 +462,14 @@ PRIVATE data/business/data_business_info.h data/business/data_shortcut_messages.cpp data/business/data_shortcut_messages.h + data/components/recent_peers.cpp + data/components/recent_peers.h + data/components/scheduled_messages.cpp + data/components/scheduled_messages.h + data/components/sponsored_messages.cpp + data/components/sponsored_messages.h + data/components/top_peers.cpp + data/components/top_peers.h data/notify/data_notify_settings.cpp data/notify/data_notify_settings.h data/notify/data_peer_notify_settings.cpp @@ -527,6 +539,8 @@ PRIVATE data/data_groups.h data/data_histories.cpp data/data_histories.h + data/data_history_messages.cpp + data/data_history_messages.h data/data_lastseen_status.h data/data_location.cpp data/data_location.h @@ -575,14 +589,10 @@ PRIVATE data/data_send_action.h data/data_session.cpp data/data_session.h - data/data_scheduled_messages.cpp - data/data_scheduled_messages.h data/data_shared_media.cpp data/data_shared_media.h data/data_sparse_ids.cpp data/data_sparse_ids.h - data/data_sponsored_messages.cpp - data/data_sponsored_messages.h data/data_statistics.h data/data_stories.cpp data/data_stories.h @@ -606,6 +616,18 @@ PRIVATE data/data_wall_paper.h data/data_web_page.cpp data/data_web_page.h + dialogs/ui/dialogs_layout.cpp + dialogs/ui/dialogs_layout.h + dialogs/ui/dialogs_message_view.cpp + dialogs/ui/dialogs_message_view.h + dialogs/ui/dialogs_stories_content.cpp + dialogs/ui/dialogs_stories_content.h + dialogs/ui/dialogs_suggestions.cpp + dialogs/ui/dialogs_suggestions.h + dialogs/ui/dialogs_topics_view.cpp + dialogs/ui/dialogs_topics_view.h + dialogs/ui/dialogs_video_userpic.cpp + dialogs/ui/dialogs_video_userpic.h dialogs/dialogs_entry.cpp dialogs/dialogs_entry.h dialogs/dialogs_indexed_list.cpp @@ -628,16 +650,6 @@ PRIVATE dialogs/dialogs_search_tags.h dialogs/dialogs_widget.cpp dialogs/dialogs_widget.h - dialogs/ui/dialogs_layout.cpp - dialogs/ui/dialogs_layout.h - dialogs/ui/dialogs_message_view.cpp - dialogs/ui/dialogs_message_view.h - dialogs/ui/dialogs_stories_content.cpp - dialogs/ui/dialogs_stories_content.h - dialogs/ui/dialogs_topics_view.cpp - dialogs/ui/dialogs_topics_view.h - dialogs/ui/dialogs_video_userpic.cpp - dialogs/ui/dialogs_video_userpic.h editor/color_picker.cpp editor/color_picker.h editor/controllers/controllers.h @@ -678,6 +690,8 @@ PRIVATE history/view/controls/compose_controls_common.h history/view/controls/history_view_compose_controls.cpp history/view/controls/history_view_compose_controls.h + history/view/controls/history_view_compose_media_edit_manager.cpp + history/view/controls/history_view_compose_media_edit_manager.h history/view/controls/history_view_compose_search.cpp history/view/controls/history_view_compose_search.h history/view/controls/history_view_draft_options.cpp @@ -720,6 +734,8 @@ PRIVATE history/view/media/history_view_media.h history/view/media/history_view_media_common.cpp history/view/media/history_view_media_common.h + history/view/media/history_view_media_generic.cpp + history/view/media/history_view_media_generic.h history/view/media/history_view_media_grouped.cpp history/view/media/history_view_media_grouped.h history/view/media/history_view_media_spoiler.cpp @@ -767,6 +783,8 @@ PRIVATE history/view/history_view_about_view.h history/view/history_view_bottom_info.cpp history/view/history_view_bottom_info.h + history/view/history_view_chat_preview.cpp + history/view/history_view_chat_preview.h history/view/history_view_contact_status.cpp history/view/history_view_contact_status.h history/view/history_view_context_menu.cpp @@ -859,14 +877,18 @@ PRIVATE history/history_view_highlight_manager.h history/history_widget.cpp history/history_widget.h - info/boosts/giveaway/giveaway_list_controllers.cpp - info/boosts/giveaway/giveaway_list_controllers.h - info/boosts/create_giveaway_box.cpp - info/boosts/create_giveaway_box.h - info/boosts/info_boosts_inner_widget.cpp - info/boosts/info_boosts_inner_widget.h - info/boosts/info_boosts_widget.cpp - info/boosts/info_boosts_widget.h + info/channel_statistics/boosts/create_giveaway_box.cpp + info/channel_statistics/boosts/create_giveaway_box.h + info/channel_statistics/boosts/giveaway/giveaway_list_controllers.cpp + info/channel_statistics/boosts/giveaway/giveaway_list_controllers.h + info/channel_statistics/boosts/info_boosts_inner_widget.cpp + info/channel_statistics/boosts/info_boosts_inner_widget.h + info/channel_statistics/boosts/info_boosts_widget.cpp + info/channel_statistics/boosts/info_boosts_widget.h + info/channel_statistics/earn/info_earn_inner_widget.cpp + info/channel_statistics/earn/info_earn_inner_widget.h + info/channel_statistics/earn/info_earn_widget.cpp + info/channel_statistics/earn/info_earn_widget.h info/common_groups/info_common_groups_inner_widget.cpp info/common_groups/info_common_groups_inner_widget.h info/common_groups/info_common_groups_widget.cpp @@ -997,6 +1019,8 @@ PRIVATE intro/intro_step.h intro/intro_widget.cpp intro/intro_widget.h + iv/iv_delegate_impl.cpp + iv/iv_delegate_impl.h iv/iv_instance.cpp iv/iv_instance.h kotato/boxes/kotato_confirm_box.cpp @@ -1150,6 +1174,8 @@ PRIVATE menu/menu_mute.h menu/menu_send.cpp menu/menu_send.h + menu/menu_sponsored.cpp + menu/menu_sponsored.h menu/menu_ttl_validator.cpp menu/menu_ttl_validator.h mtproto/config_loader.cpp @@ -1204,13 +1230,6 @@ PRIVATE payments/payments_checkout_process.h payments/payments_form.cpp payments/payments_form.h - platform/linux/linux_desktop_environment.cpp - platform/linux/linux_desktop_environment.h - platform/linux/linux_wayland_integration_dummy.cpp - platform/linux/linux_wayland_integration.cpp - platform/linux/linux_wayland_integration.h - platform/linux/linux_xdp_open_with_dialog.cpp - platform/linux/linux_xdp_open_with_dialog.h platform/linux/file_utilities_linux.cpp platform/linux/file_utilities_linux.h platform/linux/launcher_linux.cpp @@ -1312,6 +1331,10 @@ PRIVATE settings/business/settings_away_message.h settings/business/settings_shortcut_messages.cpp settings/business/settings_shortcut_messages.h + settings/business/settings_chat_intro.cpp + settings/business/settings_chat_intro.h + settings/business/settings_chat_links.cpp + settings/business/settings_chat_links.h settings/business/settings_chatbots.cpp settings/business/settings_chatbots.h settings/business/settings_greeting.cpp @@ -1462,8 +1485,6 @@ PRIVATE ui/image/image_location.h ui/image/image_location_factory.cpp ui/image/image_location_factory.h - ui/widgets/level_meter.cpp - ui/widgets/level_meter.h ui/countryinput.cpp ui/countryinput.h ui/dynamic_thumbnails.cpp @@ -1479,6 +1500,8 @@ PRIVATE ui/search_field_controller.h ui/text/format_song_document_name.cpp ui/text/format_song_document_name.h + ui/widgets/label_with_custom_emoji.cpp + ui/widgets/label_with_custom_emoji.h ui/unread_badge.cpp ui/unread_badge.h window/main_window.cpp @@ -1506,6 +1529,8 @@ PRIVATE window/window_lock_widgets.h window/window_main_menu.cpp window/window_main_menu.h + window/window_main_menu_helpers.cpp + window/window_main_menu_helpers.h window/window_media_preview.cpp window/window_media_preview.h window/window_peer_menu.cpp @@ -1514,8 +1539,6 @@ PRIVATE window/window_session_controller.cpp window/window_session_controller.h window/window_session_controller_link_info.h - window/window_slide_animation.cpp - window/window_slide_animation.h window/window_top_bar_wrap.h window/themes/window_theme.cpp window/themes/window_theme.h @@ -1559,16 +1582,6 @@ if (NOT build_winstore) ) endif() -if (DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration.cpp - ) -else() - remove_target_sources(Telegram ${src_loc} - platform/linux/linux_wayland_integration_dummy.cpp - ) -endif() - if (DESKTOP_APP_USE_PACKAGED) remove_target_sources(Telegram ${src_loc} platform/mac/mac_iconv_helper.c @@ -1696,11 +1709,6 @@ elseif (APPLE) ) endif() else() - target_link_libraries(Telegram - PRIVATE - desktop-app::external_glibmm - ) - include(${cmake_helpers_loc}/external/glib/generate_dbus.cmake) generate_dbus(Telegram org.freedesktop.portal. XdpBackground ${third_party_loc}/xdg-desktop-portal/data/org.freedesktop.portal.Background.xml) generate_dbus(Telegram org.freedesktop. XdgNotifications ${src_loc}/platform/linux/org.freedesktop.Notifications.xml) @@ -1711,19 +1719,6 @@ else() desktop-app::external_xcb ) endif() - - if (NOT DESKTOP_APP_DISABLE_WAYLAND_INTEGRATION) - qt_generate_wayland_protocol_client_sources(Telegram - FILES - ${third_party_loc}/wayland/protocol/wayland.xml - ${third_party_loc}/plasma-wayland-protocols/src/protocols/plasma-shell.xml - ) - - target_link_libraries(Telegram - PRIVATE - desktop-app::external_wayland_client - ) - endif() endif() set(bundle_identifier "io.github.kotatogram") diff --git a/Telegram/Resources/animations/chat_link.tgs b/Telegram/Resources/animations/chat_link.tgs new file mode 100644 index 000000000..21622df37 Binary files /dev/null and b/Telegram/Resources/animations/chat_link.tgs differ diff --git a/Telegram/Resources/animations/collectible_phone.tgs b/Telegram/Resources/animations/collectible_phone.tgs new file mode 100644 index 000000000..4e7284d5f Binary files /dev/null and b/Telegram/Resources/animations/collectible_phone.tgs differ diff --git a/Telegram/Resources/animations/collectible_username.tgs b/Telegram/Resources/animations/collectible_username.tgs new file mode 100644 index 000000000..f93fd6d74 Binary files /dev/null and b/Telegram/Resources/animations/collectible_username.tgs differ diff --git a/Telegram/Resources/animations/noresults.tgs b/Telegram/Resources/animations/noresults.tgs new file mode 100644 index 000000000..62a21279f Binary files /dev/null and b/Telegram/Resources/animations/noresults.tgs differ diff --git a/Telegram/Resources/animations/search.tgs b/Telegram/Resources/animations/search.tgs new file mode 100644 index 000000000..db635fb57 Binary files /dev/null and b/Telegram/Resources/animations/search.tgs differ diff --git a/Telegram/Resources/icons/chat/live_location_long.png b/Telegram/Resources/icons/chat/live_location_long.png new file mode 100644 index 000000000..847930f54 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long.png differ diff --git a/Telegram/Resources/icons/chat/live_location_long@2x.png b/Telegram/Resources/icons/chat/live_location_long@2x.png new file mode 100644 index 000000000..d4c018200 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long@2x.png differ diff --git a/Telegram/Resources/icons/chat/live_location_long@3x.png b/Telegram/Resources/icons/chat/live_location_long@3x.png new file mode 100644 index 000000000..3a8cd36b5 Binary files /dev/null and b/Telegram/Resources/icons/chat/live_location_long@3x.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png new file mode 100644 index 000000000..19f8e0b15 Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png new file mode 100644 index 000000000..ac695b71d Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@2x.png differ diff --git a/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png new file mode 100644 index 000000000..b05f0f35f Binary files /dev/null and b/Telegram/Resources/icons/dialogs/dialogs_pinned_shadow@3x.png differ diff --git a/Telegram/Resources/icons/filled_go_to_message.png b/Telegram/Resources/icons/filled_go_to_message.png new file mode 100644 index 000000000..19ad56f87 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message.png differ diff --git a/Telegram/Resources/icons/filled_go_to_message@2x.png b/Telegram/Resources/icons/filled_go_to_message@2x.png new file mode 100644 index 000000000..fdc472452 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message@2x.png differ diff --git a/Telegram/Resources/icons/filled_go_to_message@3x.png b/Telegram/Resources/icons/filled_go_to_message@3x.png new file mode 100644 index 000000000..0a6bde9b8 Binary files /dev/null and b/Telegram/Resources/icons/filled_go_to_message@3x.png differ diff --git a/Telegram/Resources/icons/menu/earn.png b/Telegram/Resources/icons/menu/earn.png new file mode 100644 index 000000000..a922012bd Binary files /dev/null and b/Telegram/Resources/icons/menu/earn.png differ diff --git a/Telegram/Resources/icons/menu/earn@2x.png b/Telegram/Resources/icons/menu/earn@2x.png new file mode 100644 index 000000000..cf8d10d34 Binary files /dev/null and b/Telegram/Resources/icons/menu/earn@2x.png differ diff --git a/Telegram/Resources/icons/menu/earn@3x.png b/Telegram/Resources/icons/menu/earn@3x.png new file mode 100644 index 000000000..8ff7dd69b Binary files /dev/null and b/Telegram/Resources/icons/menu/earn@3x.png differ diff --git a/Telegram/Resources/icons/menu/fonts.png b/Telegram/Resources/icons/menu/fonts.png new file mode 100644 index 000000000..a82aa2166 Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts.png differ diff --git a/Telegram/Resources/icons/menu/fonts@2x.png b/Telegram/Resources/icons/menu/fonts@2x.png new file mode 100644 index 000000000..5d735d58b Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts@2x.png differ diff --git a/Telegram/Resources/icons/menu/fonts@3x.png b/Telegram/Resources/icons/menu/fonts@3x.png new file mode 100644 index 000000000..0aa3679f3 Binary files /dev/null and b/Telegram/Resources/icons/menu/fonts@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro.png b/Telegram/Resources/icons/settings/premium/business/business_intro.png new file mode 100644 index 000000000..0d357a419 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png b/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png new file mode 100644 index 000000000..3e120fa5d Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png b/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png new file mode 100644 index 000000000..878abd236 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_intro@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links.png b/Telegram/Resources/icons/settings/premium/business/business_links.png new file mode 100644 index 000000000..470bc4f80 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links@2x.png b/Telegram/Resources/icons/settings/premium/business/business_links@2x.png new file mode 100644 index 000000000..42a770604 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/business/business_links@3x.png b/Telegram/Resources/icons/settings/premium/business/business_links@3x.png new file mode 100644 index 000000000..67b1f55ad Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/business/business_links@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away.png b/Telegram/Resources/icons/settings/premium/promo/business_away.png new file mode 100644 index 000000000..b18690565 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png new file mode 100644 index 000000000..24813a145 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png new file mode 100644 index 000000000..d473fdbd4 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_away@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png new file mode 100644 index 000000000..5e71b3b93 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png new file mode 100644 index 000000000..ba588c6ec Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png new file mode 100644 index 000000000..615d94b27 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatbot@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png new file mode 100644 index 000000000..5b812d8ec Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png new file mode 100644 index 000000000..6490fb474 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png new file mode 100644 index 000000000..af17dbc53 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_chatlink@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting.png new file mode 100644 index 000000000..22d1526c2 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png new file mode 100644 index 000000000..21c575d73 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png new file mode 100644 index 000000000..3bf932cd6 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_greeting@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours.png b/Telegram/Resources/icons/settings/premium/promo/business_hours.png new file mode 100644 index 000000000..ea917bb24 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png new file mode 100644 index 000000000..62411855b Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png new file mode 100644 index 000000000..923950f84 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_hours@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro.png b/Telegram/Resources/icons/settings/premium/promo/business_intro.png new file mode 100644 index 000000000..57d1c8c82 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png new file mode 100644 index 000000000..4132668cc Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png new file mode 100644 index 000000000..587b4e36e Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_intro@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location.png b/Telegram/Resources/icons/settings/premium/promo/business_location.png new file mode 100644 index 000000000..074f44184 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png new file mode 100644 index 000000000..abc79b9b9 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png new file mode 100644 index 000000000..1d58a00bc Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_location@3x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png new file mode 100644 index 000000000..91282589d Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png new file mode 100644 index 000000000..90662ccf9 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@2x.png differ diff --git a/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png new file mode 100644 index 000000000..ebe964f29 Binary files /dev/null and b/Telegram/Resources/icons/settings/premium/promo/business_quickreply@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/channel.png b/Telegram/Resources/icons/sponsored/channel.png new file mode 100644 index 000000000..2f66a5911 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel.png differ diff --git a/Telegram/Resources/icons/sponsored/channel@2x.png b/Telegram/Resources/icons/sponsored/channel@2x.png new file mode 100644 index 000000000..765e4cf31 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/channel@3x.png b/Telegram/Resources/icons/sponsored/channel@3x.png new file mode 100644 index 000000000..21a5c1f9e Binary files /dev/null and b/Telegram/Resources/icons/sponsored/channel@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about.png b/Telegram/Resources/icons/sponsored/large_about.png new file mode 100644 index 000000000..95fb27be4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@2x.png b/Telegram/Resources/icons/sponsored/large_about@2x.png new file mode 100644 index 000000000..8af8a580f Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_about@3x.png b/Telegram/Resources/icons/sponsored/large_about@3x.png new file mode 100644 index 000000000..94d8147c2 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn.png b/Telegram/Resources/icons/sponsored/large_earn.png new file mode 100644 index 000000000..c41ac8378 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn@2x.png b/Telegram/Resources/icons/sponsored/large_earn@2x.png new file mode 100644 index 000000000..79232dd2b Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/large_earn@3x.png b/Telegram/Resources/icons/sponsored/large_earn@3x.png new file mode 100644 index 000000000..2d722c06c Binary files /dev/null and b/Telegram/Resources/icons/sponsored/large_earn@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about.png b/Telegram/Resources/icons/sponsored/privacy_about.png new file mode 100644 index 000000000..67fc01990 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@2x.png b/Telegram/Resources/icons/sponsored/privacy_about@2x.png new file mode 100644 index 000000000..e63d296dc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/privacy_about@3x.png b/Telegram/Resources/icons/sponsored/privacy_about@3x.png new file mode 100644 index 000000000..3b95f5bc6 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/privacy_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about.png b/Telegram/Resources/icons/sponsored/remove_about.png new file mode 100644 index 000000000..06a33dc3c Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@2x.png b/Telegram/Resources/icons/sponsored/remove_about@2x.png new file mode 100644 index 000000000..996851dbc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/remove_about@3x.png b/Telegram/Resources/icons/sponsored/remove_about@3x.png new file mode 100644 index 000000000..0863b33e8 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/remove_about@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split.png b/Telegram/Resources/icons/sponsored/revenue_split.png new file mode 100644 index 000000000..94390bce4 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@2x.png b/Telegram/Resources/icons/sponsored/revenue_split@2x.png new file mode 100644 index 000000000..23a23ff0a Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/revenue_split@3x.png b/Telegram/Resources/icons/sponsored/revenue_split@3x.png new file mode 100644 index 000000000..4086a7714 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/revenue_split@3x.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals.png b/Telegram/Resources/icons/sponsored/withdrawals.png new file mode 100644 index 000000000..4baf224d7 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals@2x.png b/Telegram/Resources/icons/sponsored/withdrawals@2x.png new file mode 100644 index 000000000..93d2225f1 Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals@2x.png differ diff --git a/Telegram/Resources/icons/sponsored/withdrawals@3x.png b/Telegram/Resources/icons/sponsored/withdrawals@3x.png new file mode 100644 index 000000000..8827877fc Binary files /dev/null and b/Telegram/Resources/icons/sponsored/withdrawals@3x.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph.png b/Telegram/Resources/icons/statistics/mini_currency_graph.png new file mode 100644 index 000000000..cce4957b3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png new file mode 100644 index 000000000..b48cf0572 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@2x.png differ diff --git a/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png new file mode 100644 index 000000000..14cdf7ca3 Binary files /dev/null and b/Telegram/Resources/icons/statistics/mini_currency_graph@3x.png differ diff --git a/Telegram/Resources/iv_html/page.css b/Telegram/Resources/iv_html/page.css index bdc44f184..9c0ab832f 100644 --- a/Telegram/Resources/iv_html/page.css +++ b/Telegram/Resources/iv_html/page.css @@ -1,5 +1,11 @@ +:root { + --font-sans: -apple-system, BlinkMacSystemFont, avenir next, avenir, Segoe UI Variable Text, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, tahoma, arial, sans-serif; + --font-serif: Iowan Old Style, Apple Garamond, Baskerville, Georgia, Times New Roman, Droid Serif, Times, Source Serif Pro, serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + --font-mono: Menlo, Cascadia Code, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; +} + body { - font-family: 'Helvetica Neue'; + font-family: var(--font-sans); font-size: 17px; line-height: 25px; padding: 0; @@ -82,6 +88,12 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover { opacity: 1; } } +#menu_page_blocker { + z-index: 999; + position: fixed; + width: 100%; + height: 100%; +} #top_shadow { z-index: 999; position: fixed; @@ -122,9 +134,32 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover { .page-slide { position: relative; width: 100%; + min-height: 100%; margin-left: 0%; transition: margin 240ms ease-in-out; } +.page-footer { + height: 32px; + margin-top: -32px; + background: var(--td-window-bg-over); +} +.page-footer .content { + padding: 3px 18px; + font-size: 15px; + color: var(--td-window-sub-text-fg); + text-align: center; +} +.page-footer .wrong { + position: relative; + padding: 5px; + margin: -5px; + color: var(--td-window-sub-text-fg); + text-decoration: none; + cursor: pointer; +} +.page-footer .wrong:hover { + text-decoration: underline; +} .hidden-left, .hidden-right { pointer-events: none; @@ -136,7 +171,7 @@ html.custom_scroll ::-webkit-scrollbar-thumb:hover { margin-left: 100%; } article { - padding-bottom: 12px; + padding-bottom: 40px; overflow-y: hidden; overflow-x: auto; white-space: pre-wrap; @@ -145,7 +180,7 @@ article { } article h1, article h2 { - font-family: 'Georgia'; + font-family: var(--font-serif); font-size: 28px; line-height: 31px; margin: 21px 18px 12px; @@ -237,10 +272,10 @@ article ol p { } article pre, article pre.hljs { - font-family: Menlo; + font-family: var(--font-mono); margin: 14px 0; padding: 7px 18px; - background: #F5F8FC; + background: var(--td-box-divider-bg); font-size: 16px; white-space: pre-wrap; word-wrap: break-word; @@ -265,7 +300,7 @@ article pre + pre { } article h3, article h4 { - font-family: 'Georgia'; + font-family: var(--font-serif); font-size: 24px; line-height: 30px; margin: 18px 18px 9px; @@ -284,7 +319,7 @@ article ol h4 { margin: 10px 0 5px; } article blockquote { - font-family: 'Georgia'; + font-family: var(--font-serif); margin: 18px 18px 16px; padding-left: 22px; position: relative; @@ -310,7 +345,7 @@ article.rtl blockquote:before { left: auto; } article aside { - font-family: 'Georgia'; + font-family: var(--font-serif); margin: 18px 18px 16px; padding: 0 18px; text-align: center; @@ -326,7 +361,7 @@ article blockquote cite, article aside cite, article footer cite, article .pullquote cite { - font-family: 'Helvetica Neue'; + font-family: var(--font-sans); font-size: 15px; display: block; color: var(--td-window-sub-text-fg); @@ -601,7 +636,7 @@ ol figcaption { padding-right: 0; } figcaption > cite { - font-family: 'Helvetica Neue'; + font-family: var(--font-sans); font-size: 12px; display: block; line-height: 15px; @@ -640,7 +675,7 @@ figure.slideshow .video-wrap { bottom: 10px; white-space: nowrap; overflow: hidden; - z-index: 3; + z-index: 5; } .slideshow-buttons > fieldset { padding: 0; @@ -855,7 +890,7 @@ section.related { margin: 7px 0 12px; } section.related h4 { - font-family: 'Helvetica Neue'; + font-family: var(--font-sans); font-size: 17px; line-height: 26px; font-weight: 500; @@ -881,6 +916,9 @@ section.related a.related-link:after { right: 0; bottom: 0; } +section.related a.related-link:last-child:after { + border-bottom: 0px; +} section.related .related-link-url { display: block; font-size: 15px; @@ -993,10 +1031,10 @@ section.channel > a > div.join:hover { text-decoration: underline; } section.channel > a > div.join span:before { - content: var(--td-lng-group-call-join); + content: var(--td-lng-iv-join-channel); } section.channel > a > h4 { - font-family: 'Helvetica Neue'; + font-family: var(--font-sans); font-size: 17px; line-height: 26px; font-weight: 500; @@ -1015,6 +1053,9 @@ section.channel > a > h4 { display: block; margin: 0 auto; } +.media-outer { + margin-bottom: 16px; +} .photo-wrap, .video-wrap { width: 100%; diff --git a/Telegram/Resources/iv_html/page.js b/Telegram/Resources/iv_html/page.js index efac9fa65..bae02fe48 100644 --- a/Telegram/Resources/iv_html/page.js +++ b/Telegram/Resources/iv_html/page.js @@ -8,6 +8,11 @@ var IV = { var target = e.target; var context = ''; while (target) { + if (target.id == 'menu_page_blocker') { + IV.notify({ event: 'menu_page_blocker_click' }); + IV.menuShown(false); + return; + } if (target.tagName == 'AUDIO' || target.tagName == 'VIDEO') { return; } @@ -21,7 +26,7 @@ var IV = { } target = target.parentNode; } - if (!target || !target.hasAttribute('href')) { + if (!target || (context === '' && !target.hasAttribute('href'))) { return; } var base = document.createElement('A'); @@ -37,7 +42,7 @@ var IV = { } else if (target.hash.length < 2) { IV.jumpToHash(''); } else { - IV.jumpToHash(target.hash.substr(1)); + IV.jumpToHash(decodeURIComponent(target.hash.substr(1))); } e.preventDefault(); }, @@ -122,12 +127,17 @@ var IV = { } }, toggleChannelJoined: function (id, joined) { + IV.channelsJoined['channel' + id] = joined; + IV.checkChannelButtons(); + }, + checkChannelButtons: function() { const channels = document.getElementsByClassName('channel'); - const full = 'channel' + id; for (var i = 0; i < channels.length; ++i) { const channel = channels[i]; - if (String(channel.getAttribute('data-context')) === full) { - channel.classList.toggle('joined', joined); + const full = String(channel.getAttribute('data-context')); + const value = IV.channelsJoined[full]; + if (value !== undefined) { + channel.classList.toggle('joined', value); } } }, @@ -139,7 +149,6 @@ var IV = { var s = form.s; const next = +s.value + delta; s.value = (next == s.length) ? 0 : (next == -1) ? (s.length - 1) : next; - s.forEach(function(el){ el.checked && el.parentNode.scrollIntoView && el.parentNode.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'center'}); }); form.nextSibling.firstChild.style[marginProp] = (-100 * s.value) + '%'; } else { el.form.nextSibling.firstChild.style[marginProp] = (-100 * el.value) + '%'; @@ -320,6 +329,7 @@ var IV = { + '" type="video/mp4" />' + ''; var media = element.firstChild; + media.oncontextmenu = function () { return false; }; media.oncanplay = IV.checkVideos; media.onloadeddata = IV.checkVideos; } @@ -403,9 +413,12 @@ var IV = { var article = function (el) { return el.getElementsByTagName('article')[0]; }; - var from = article(IV.findPageScroll()); - var to = article(IV.makeScrolledContent(data.html)); - morphdom(from, to, { + var footer = function (el) { + return el.getElementsByClassName('page-footer')[0]; + }; + var from = IV.findPageScroll(); + var to = IV.makeScrolledContent(data.html); + morphdom(article(from), article(to), { onBeforeElUpdated: function (fromEl, toEl) { if (fromEl.classList.contains('video') && toEl.classList.contains('video') @@ -414,12 +427,22 @@ var IV = { && (fromEl.getAttribute('data-src') == toEl.getAttribute('data-src'))) { return false; + } else if (fromEl.tagName == 'SECTION' + && fromEl.classList.contains('channel') + && fromEl.hasAttribute('data-context') + && toEl.tagName == 'SECTION' + && toEl.classList.contains('channel') + && toEl.hasAttribute('data-context') + && (String(fromEl.getAttribute('data-context')) + == String(toEl.getAttribute('data-context')))) { + return false; } else if (fromEl.classList.contains('loaded')) { toEl.classList.add('loaded'); } return !fromEl.isEqualNode(toEl); } }); + morphdom(footer(from), footer(to)); IV.initMedia(); eval(data.js); }, @@ -458,9 +481,7 @@ var IV = { var result = document.createElement('div'); result.className = 'page-scroll'; result.tabIndex = '-1'; - result.innerHTML = '
' - + html - + '
'; + result.innerHTML = html.trim(); result.onscroll = IV.frameScrolled; return result; }, @@ -568,6 +589,7 @@ var IV = { } else { IV.initMedia(); } + IV.checkChannelButtons(); if (scroll === undefined) { IV.jumpToHash(hash, true); } else { @@ -599,11 +621,26 @@ var IV = { back: function () { window.history.back(); }, + menuShown: function (shown) { + var already = document.getElementById('menu_page_blocker'); + if (already && shown) { + return; + } else if (already) { + document.body.removeChild(already); + return; + } else if (!shown) { + return; + } + var blocker = document.createElement('div'); + blocker.id = 'menu_page_blocker'; + document.body.appendChild(blocker); + }, videos: {}, videosPlaying: {}, cache: {}, + channelsJoined: {}, index: 0, position: 0 }; diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 2d0f0c406..aaf76cac0 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -19,6 +19,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_menu_set_status" = "Set Emoji Status"; "lng_menu_change_status" = "Change Emoji Status"; "lng_menu_my_stories" = "My Stories"; +"lng_menu_my_groups" = "My Groups"; +"lng_menu_my_channels" = "My Channels"; "lng_disable_notifications_from_tray" = "Disable notifications"; "lng_enable_notifications_from_tray" = "Enable notifications"; @@ -307,7 +309,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_limit_reached#one" = "You've reached the message text limit. Please make the text shorter by {count} character."; "lng_edit_limit_reached#other" = "You've reached the message text limit. Please make the text shorter by {count} characters."; "lng_edit_message" = "Edit message"; -"lng_edit_message_text" = "New message text..."; +"lng_edit_message_text" = "Caption"; "lng_deleted" = "Deleted Account"; "lng_deleted_message" = "Deleted message"; "lng_deleted_story" = "Deleted story"; @@ -469,6 +471,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bio_placeholder" = "Bio"; +"lng_collectible_username_title" = "{username} is a collectible username that belongs to"; +"lng_collectible_username_info" = "This username was bought on **Fragment** on {date} for {price}"; +"lng_collectible_username_copy" = "Copy Link"; +"lng_collectible_phone_title" = "{phone} is a collectible phone number that belongs to"; +"lng_collectible_phone_info" = "This phone number was bought on **Fragment** on {date} for {price}"; +"lng_collectible_phone_copy" = "Copy Phone Number"; +"lng_collectible_learn_more" = "Learn More"; + "lng_settings_section_info" = "My info"; "lng_settings_section_notify" = "Notifications"; @@ -652,6 +662,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_messages_privacy" = "Messages"; "lng_settings_voices_privacy" = "Voice messages"; "lng_settings_bio_privacy" = "Bio"; +"lng_settings_birthday_privacy" = "Date of Birth"; "lng_settings_privacy_premium" = "Only subscribers of {link} can restrict receiving voice messages."; "lng_settings_privacy_premium_link" = "Telegram Premium"; "lng_settings_passcode_disable" = "Disable Passcode"; @@ -664,6 +675,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_phone_label" = "Phone number"; "lng_settings_username_add" = "Add username"; "lng_settings_username_about" = "Username lets people contact you on Telegram without needing your phone number."; +"lng_settings_birthday_label" = "Date of Birth"; +"lng_settings_birthday_add" = "Add"; +"lng_settings_birthday_about" = "Choose who can see your birthday in {link}."; +"lng_settings_birthday_about_link" = "Settings"; +"lng_settings_birthday_contacts" = "Only your contacts can see your birthday. {link}"; +"lng_settings_birthday_contacts_link" = "Change >"; +"lng_settings_birthday_saved" = "Your date of birth was updated."; +"lng_settings_birthday_reset" = "Remove"; +"lng_settings_channel_label" = "Personal channel"; +"lng_settings_channel_add" = "Add"; +"lng_settings_channel_remove" = "Remove"; +"lng_settings_channel_no_yet" = "You don't have any public channels yet."; +"lng_settings_channel_start" = "Start a Channel"; +"lng_settings_channel_saved" = "Your personal channel was updated."; +"lng_settings_channel_removed" = "Your personal channel was removed."; "lng_settings_add_account_about" = "You can add up to four accounts with different phone numbers."; "lng_settings_peer_to_peer_about" = "Disabling peer-to-peer will relay all calls through Telegram servers to avoid revealing your IP address, but may slightly decrease audio quality."; "lng_settings_advanced" = "Advanced"; @@ -704,10 +730,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_angle_backend_d3d11on12" = "D3D11on12"; "lng_settings_angle_backend_opengl" = "OpenGL"; "lng_settings_angle_backend_disabled" = "Disabled"; +"lng_settings_top_peers_title" = "Frequent contacts"; +"lng_settings_top_peers_suggest" = "Suggest frequent contacts"; +"lng_settings_top_peers_about" = "Display people you message frequently at the top of the search section for quick access."; "lng_settings_sensitive_title" = "Sensitive content"; "lng_settings_sensitive_disable_filtering" = "Disable filtering"; "lng_settings_sensitive_about" = "Display sensitive media in public channels on all your Telegram devices."; "lng_settings_security_bots" = "Bots and websites"; +"lng_settings_file_confirmations" = "File open confirmations"; +"lng_settings_edit_extensions" = "Extensions whitelist"; +"lng_settings_edit_extensions_about" = "Open files with the following extensions without additional confirmation."; +"lng_settings_edit_ip_confirm" = "IP reveal warning"; +"lng_settings_edit_ip_confirm_about" = "Show confirmation when opening files that may reveal your IP address."; "lng_settings_clear_payment_info" = "Clear Payment and Shipping Info"; "lng_settings_logged_in" = "Connected websites"; "lng_settings_logged_in_title" = "Logged in with Telegram"; @@ -809,6 +843,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_auto_night_mode_on" = "System"; "lng_settings_auto_night_warning" = "You have enabled auto-night mode. If you want to change the dark mode settings, you'll need to disable it first."; "lng_settings_auto_night_disable" = "Disable"; +"lng_settings_font_family" = "Font family"; "lng_settings_color_title" = "Color preview"; "lng_settings_color_reply" = "Reply to your message"; @@ -1018,6 +1053,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_proxy_sponsor" = "Proxy sponsor"; "lng_proxy_sponsor_about" = "This channel is shown by your proxy server.\nTo remove this channel from your chats list,\ndisable the proxy in Telegram Settings."; "lng_proxy_sponsor_warning" = "This proxy may display a sponsored channel in your chat list. This doesn't reveal any of your Telegram traffic."; +"lng_proxy_add_from_clipboard" = "Add proxy from clipboard"; +"lng_proxy_add_from_clipboard_good_toast" = "Proxy was added from clipboard."; +"lng_proxy_add_from_clipboard_failed_toast" = "This is not a proxy link."; +"lng_proxy_add_from_clipboard_existing_toast" = "This proxy is already in the list."; "lng_badge_psa_default" = "PSA"; "lng_about_psa_default" = "This message provides you with a public service announcement. To remove it from your chats list, right click it and select **Hide**."; "lng_tooltip_psa_default" = "This message provides you with a public service announcement."; @@ -1042,6 +1081,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_faq_button" = "Go to FAQ"; "lng_settings_ask_ok" = "Ask a Volunteer"; "lng_settings_faq" = "Telegram FAQ"; +"lng_settings_faq_link" = "https://telegram.org/faq#general-questions"; "lng_settings_features" = "Telegram Features"; "lng_settings_logout" = "Log Out"; "lng_sure_logout" = "Are you sure you want to log out?"; @@ -1087,12 +1127,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_everyone" = "Everybody"; "lng_edit_privacy_contacts" = "My contacts"; "lng_edit_privacy_close_friends" = "Close friends"; +"lng_edit_privacy_contacts_and_premium" = "Contacts & Premium"; "lng_edit_privacy_nobody" = "Nobody"; "lng_edit_privacy_premium" = "Premium users"; "lng_edit_privacy_exceptions" = "Add exceptions"; +"lng_edit_privacy_user_types" = "User types"; +"lng_edit_privacy_users_and_groups" = "Users and groups"; +"lng_edit_privacy_premium_status" = "all Telegram Premium subscribers"; "lng_edit_privacy_exceptions_count#one" = "{count} user"; "lng_edit_privacy_exceptions_count#other" = "{count} users"; +"lng_edit_privacy_exceptions_premium_and" = "Premium & {users}"; "lng_edit_privacy_exceptions_add" = "Add users or groups"; "lng_edit_privacy_phone_number_title" = "Phone number privacy"; @@ -1136,6 +1181,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_edit_privacy_about_always_title" = "Always allow"; "lng_edit_privacy_about_never_title" = "Never allow"; +"lng_edit_privacy_birthday_title" = "Date of birth privacy"; +"lng_edit_privacy_birthday_header" = "Who can see my date of birth"; +"lng_edit_privacy_birthday_always_empty" = "Always allow"; +"lng_edit_privacy_birthday_never_empty" = "Never allow"; +"lng_edit_privacy_birthday_exceptions" = "These users will or will not be able to see your date of birth regardless of the settings above."; +"lng_edit_privacy_birthday_always_title" = "Always allow"; +"lng_edit_privacy_birthday_never_title" = "Never allow"; +"lng_edit_privacy_birthday_yet" = "You haven't entered your date of birth yet.\n{link}"; +"lng_edit_privacy_birthday_yet_link" = "Add my birthday >"; + "lng_edit_privacy_calls_title" = "Voice calls privacy"; "lng_edit_privacy_calls_header" = "Who can call you"; "lng_edit_privacy_calls_always_empty" = "Always allow"; @@ -1288,6 +1343,24 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_profile_photo_by_you" = "photo set by you"; "lng_profile_public_photo" = "public photo"; +"lng_invite_upgrade_title" = "Upgrade to Premium"; +"lng_invite_upgrade_group_invite#one" = "{users} only accepts invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_invite#other" = "{users} only accept invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_write#one" = "{users} only accepts messages and invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_group_write#other" = "{users} only accept messages and invitations to groups from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_invite#one" = "{users} only accepts invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_invite#other" = "{users} only accept invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_write#one" = "{users} only accepts messages and invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_channel_write#other" = "{users} only accept messages and invitations to channels from Contacts and **Premium** users."; +"lng_invite_upgrade_users_few" = "{users} and {last}"; +"lng_invite_upgrade_users_many#one" = "{users} and **{count}** more person"; +"lng_invite_upgrade_users_many#other" = "{users} and **{count}** more people"; +"lng_invite_upgrade_or" = "or"; +"lng_invite_upgrade_via_title" = "Invite via Link"; +"lng_invite_upgrade_via_group_about" = "You can send an invite link to the group in a private message instead."; +"lng_invite_upgrade_via_channel_about" = "You can send an invite link to the channel in a private message instead."; +"lng_invite_status_disabled" = "available only to Premium users"; + "lng_via_link_group_one" = "**{user}** restricts adding them to groups.\nYou can send them an invite link as message instead."; "lng_via_link_group_many#one" = "**{count} user** restricts adding them to groups.\nYou can send them an invite link as message instead."; "lng_via_link_group_many#other" = "**{count} users** restrict adding them to groups.\nYou can send them an invite link as message instead."; @@ -1303,12 +1376,20 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_via_link_shared_many#one" = "Link shared with **{count} user**."; "lng_via_link_shared_many#other" = "Link shared with **{count} users**."; +"lng_info_personal_channel_label" = "Channel"; "lng_info_mobile_label" = "Mobile"; "lng_info_mobile_context_menu_fragment_about" = "This number is not tied to a SIM card and was acquired on {link}."; "lng_info_mobile_context_menu_fragment_about_link" = "Fragment"; "lng_info_mobile_hidden" = "Hidden"; "lng_info_username_label" = "Username"; "lng_info_usernames_label" = "also"; +"lng_info_birthday_label" = "Date of birth"; +"lng_info_birthday_years#one" = "{date} ({count} year old)"; +"lng_info_birthday_years#other" = "{date} ({count} years old)"; +"lng_info_birthday_today_years#one" = "{date}\n({count} year old)"; +"lng_info_birthday_today_years#other" = "{date}\n({count} years old)"; +"lng_info_birthday_today_label" = "Birthday today"; +"lng_info_birthday_today" = "{emoji} {date}"; "lng_info_bio_label" = "Bio"; "lng_info_link_label" = "Link"; "lng_info_location_label" = "Location"; @@ -1419,6 +1500,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_reactions_none_about" = "Members of the group can't add any reactions to messages."; "lng_manage_peer_reactions_some_title" = "Only allow these reactions"; "lng_manage_peer_reactions_available" = "Available reactions"; +"lng_manage_peer_reactions_available_ph" = "Add reactions..."; "lng_manage_peer_reactions_own" = "You can also {link} emoji packs and use them as reactions."; "lng_manage_peer_reactions_own_link" = "create your own"; "lng_manage_peer_reactions_level#one" = "Your channel needs to reach level **{count}** to use **{same_count}** custom reaction."; @@ -1427,6 +1509,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_reactions_boost_link" = "here"; "lng_manage_peer_reactions_limit" = "Channels can't have more custom reactions."; +"lng_manage_peer_reactions_max_title" = "Maximum number of reactions"; +"lng_manage_peer_reactions_max_slider#one" = "{count} reaction per post"; +"lng_manage_peer_reactions_max_slider#other" = "{count} reactions per post"; +"lng_manage_peer_reactions_max_about" = "Limit the number of different reactions that can be added to a post, including already published ones."; + "lng_manage_peer_antispam" = "Aggressive Anti-Spam"; "lng_manage_peer_antispam_about" = "Telegram will filter more spam but may occasionally affect ordinary messages. You can report False Positives in Recent Actions."; "lng_manage_peer_antispam_not_enough#one" = "Aggressive filtering can be enabled only in groups with more than **{count} member**."; @@ -1439,8 +1526,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_manage_peer_link_invite" = "Invite link"; "lng_manage_peer_link_expired" = "Expired link"; "lng_manage_private_group_title" = "Private"; +"lng_manage_private_group_noforwards_title" = "Private restricted"; "lng_manage_public_group_title" = "Public"; "lng_manage_private_peer_title" = "Private"; +"lng_manage_private_peer_noforwards_title" = "Private restricted"; "lng_manage_public_peer_title" = "Public"; "lng_manage_peer_send_title" = "Who can send new messages?"; "lng_manage_peer_send_only_members" = "Only members"; @@ -1540,6 +1629,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_report_button" = "Report"; "lng_report_thanks" = "Thank you! Your report will be reviewed by our team very soon."; +"lng_report_sponsored_hidden" = "Sponsored messages will be hidden."; +"lng_report_sponsored_reported" = "We will review this ad to ensure it matches out {link}."; +"lng_report_sponsored_reported_link" = "Ad Policies and Guidelines"; +"lng_report_sponsored_reported_learn" = "Learn more about our {link}."; + "lng_channel_add_members" = "Add members"; "lng_channel_add_users" = "Add users"; "lng_channel_add_removed" = "Remove user"; @@ -1760,6 +1854,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_action_giveaway_results_none" = "No winners of the giveaway could be selected."; "lng_action_boost_apply#one" = "{from} boosted the group"; "lng_action_boost_apply#other" = "{from} boosted the group {count} times"; +"lng_action_set_chat_intro" = "{from} added the message below for all empty chats. How?"; "lng_similar_channels_title" = "Similar channels"; "lng_similar_channels_view_all" = "View all"; @@ -1919,6 +2014,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_forwarded_imported" = "This message was imported from another app. It may not be real."; "lng_signed_author" = "Author: {user}"; "lng_sponsored_message_title" = "Sponsored"; +"lng_sponsored_message_revenue_button" = "what's this?"; "lng_recommended_message_title" = "Recommended"; "lng_edited" = "edited"; "lng_commented" = "commented"; @@ -2185,6 +2281,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_business_about_away_messages" = "Define messages that are automatically sent when you are off."; "lng_business_subtitle_chatbots" = "Chatbots"; "lng_business_about_chatbots" = "Add any third party chatbots that will process customer interactions."; +"lng_business_subtitle_chat_intro" = "Custom Intro"; +"lng_business_about_chat_intro" = "Customize the message people see before they start a chat with you."; +"lng_business_subtitle_chat_links" = "Links to Chat"; +"lng_business_about_chat_links" = "Create links that start a chat with you, suggesting the first message."; +"lng_business_subtitle_sponsored" = "Ads in Channels"; +"lng_business_button_sponsored" = "Do Not Hide Ads"; +"lng_business_about_sponsored" = "As a Premium subscriber, you don’t see any ads on Telegram, but you can turn them on, for example, to view your own ads that you launched on the {link}"; +"lng_business_about_sponsored_link" = "Telegram Ad Platform {emoji}"; +"lng_business_about_sponsored_url" = "https://ads.telegram.org"; "lng_location_title" = "Location"; "lng_location_about" = "Display the location of your business on your account."; @@ -2286,8 +2391,52 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_chatbots_reply_about" = "The bot will be able to view all new incoming messages, but not the messages that had been sent before you added the bot."; "lng_chatbots_remove" = "Remove Bot"; "lng_chatbots_not_found" = "Chatbot not found."; +"lng_chatbots_not_supported" = "This bot doesn't support Telegram Business yet."; "lng_chatbots_add" = "Add"; "lng_chatbots_info_url" = "https://telegram.org/privacy"; +"lng_chatbot_status_can_reply" = "bot manages this chat"; +"lng_chatbot_status_paused" = "bot stopped"; +"lng_chatbot_status_views" = "bot has access to this chat"; +"lng_chatbot_button_pause" = "Stop"; +"lng_chatbot_button_resume" = "Start"; +"lng_chatbot_menu_manage" = "Manage bot"; +"lng_chatbot_menu_remove" = "Remove bot from this chat"; +"lng_chatbot_menu_revoke" = "Revoke access to this chat"; + +"lng_chat_intro_title" = "Custom Intro"; +"lng_chat_intro_subtitle" = "Customize your intro"; +"lng_chat_intro_default_title" = "No messages here yet..."; +"lng_chat_intro_default_message" = "Send a message or click on the greeting below"; +"lng_chat_intro_enter_title" = "Enter Title"; +"lng_chat_intro_enter_message" = "Enter Message"; +"lng_chat_intro_choose_sticker" = "Choose Sticker"; +"lng_chat_intro_random_sticker" = "Random"; +"lng_chat_intro_about" = "You can customize the message people see before they start a chat with you."; +"lng_chat_intro_reset" = "Reset to Default"; + +"lng_chat_links_title" = "Links to Chat"; +"lng_chat_links_about" = "Give your customers short links that start a chat with you – and suggest the first message from them to you."; +"lng_chat_links_create_link" = "Create a Link to Chat"; +"lng_chat_links_footer" = "You can also use a simple link for a chat with you – {links}"; +"lng_chat_links_footer_both" = "{username} or {link}"; +"lng_chat_links_no_clicks" = "no clicks"; +"lng_chat_links_clicks#one" = "{count} click"; +"lng_chat_links_clicks#other" = "{count} clicks"; +"lng_chat_link_new_title" = "New Link"; +"lng_chat_link_edit_title" = "Edit Link"; +"lng_chat_link_description" = "Add a message that will be entered in the message field for anyone who starts a chat with you using this link."; +"lng_chat_link_placeholder" = "Add Preset Message"; +"lng_chat_link_saved" = "Chat link saved."; +"lng_chat_link_copy" = "Copy"; +"lng_chat_link_share" = "Share"; +"lng_chat_link_rename" = "Rename"; +"lng_chat_link_delete" = "Delete"; +"lng_chat_link_name" = "Link Name (optional)"; +"lng_chat_link_name_about" = "Add a name for this link that only you will see."; +"lng_chat_link_delete_sure" = "Are you sure you want to delete this chat link?"; +"lng_chat_link_qr_title" = "Chat Link QR Code"; +"lng_chat_link_qr_about" = "Everyone on Telegram can scan this code to contact you."; +"lng_chat_link_copied" = "Chat link copied to clipboard."; "lng_boost_channel_button" = "Boost Channel"; "lng_boost_group_button" = "Boost Group"; @@ -2387,6 +2536,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boost_channel_needs_level_reactions#one" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as a reaction."; "lng_boost_channel_needs_level_reactions#other" = "Your channel needs to reach **Level {count}** to add **{same_count}** custom emoji as reactions."; +"lng_boost_channel_title_cpm" = "Boost Channel"; +"lng_boost_channel_needs_level_cpm#one" = "Your channel needs to reach **Level {count}** to switch off ads."; +"lng_boost_channel_needs_level_cpm#other" = "Your channel needs to reach **Level {count}** to switch off ads."; + "lng_boost_group_title_emoji" = "Enable emoji pack"; "lng_boost_group_needs_level_emoji#one" = "Your group needs to reach **Level {count}** to set emoji pack."; "lng_boost_group_needs_level_emoji#other" = "Your group needs to reach **Level {count}** to set emoji pack."; @@ -2593,6 +2746,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_gift_link_also_send" = "You can also {link} to a friend as a gift."; "lng_gift_link_also_send_link" = "send this link"; "lng_gift_link_use" = "Use Link"; +"lng_gift_link_already_title" = "You already have Telegram Premium"; +"lng_gift_link_already_about" = "You can activate this link after {date} or {link} to a friend."; +"lng_gift_link_already_link" = "send the link"; "lng_gift_link_used_title" = "Used Gift Link"; "lng_gift_link_used_about" = "This link was used to activate\na **Telegram Premium** subscription."; "lng_gift_link_used_footer" = "This link was used on {date}."; @@ -2704,7 +2860,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_in_dlg_audio_count#other" = "{count} audio"; "lng_ban_user" = "Ban User"; +"lng_ban_users" = "Ban users"; +"lng_restrict_users" = "Restrict users"; "lng_delete_all_from_user" = "Delete all from {user}"; +"lng_delete_all_from_users" = "Delete all from users"; +"lng_restrict_user_part" = "Partially restrict this user {emoji}"; +"lng_restrict_users_part" = "Partially restrict users {emoji}"; +"lng_restrict_user_full" = "Fully ban this user {emoji}"; +"lng_restrict_users_full" = "Fully ban users {emoji}"; +"lng_restrict_users_part_single_header" = "What can this user do?"; +"lng_restrict_users_part_header#one" = "What can {count} selected user do?"; +"lng_restrict_users_part_header#other" = "What can {count} selected users do?"; "lng_report_spam" = "Report Spam"; "lng_report_spam_and_leave" = "Report spam and leave"; "lng_report_spam_done" = "Thank you for your report."; @@ -2884,6 +3050,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_bot_remove_from_side_menu_done" = "Bot removed from the main menu."; "lng_bot_settings" = "Settings"; "lng_bot_open" = "Open Bot"; +"lng_bot_terms" = "Terms of Use"; "lng_bot_reload_page" = "Reload Page"; "lng_bot_add_to_menu" = "{bot} asks your permission to be added as an option to your attachments menu so you can access it from any chat."; "lng_bot_add_to_menu_done" = "Bot added to the menu."; @@ -2932,6 +3099,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_unread_bar_some" = "Unread messages"; "lng_maps_point" = "Location"; +"lng_live_location" = "Live Location"; +"lng_live_location_now" = "updated just now"; +"lng_live_location_minutes#one" = "updated {count} minute ago"; +"lng_live_location_minutes#other" = "updated {count} minutes ago"; +"lng_live_location_hours#one" = "updated {count} hour ago"; +"lng_live_location_hours#other" = "updated {count} hours ago"; +"lng_live_location_today" = "updated today at {time}"; +"lng_live_location_yesterday" = "updated yesterday at {time}"; +"lng_live_location_date_time" = "updated {date} at {time}"; "lng_save_photo" = "Save image"; "lng_save_video" = "Save video"; "lng_save_audio_file" = "Save audio file"; @@ -3014,6 +3190,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_context_delete_msg" = "Delete Message"; "lng_context_auto_delete_in" = "auto-delete in {duration}"; "lng_context_select_msg" = "Select Message"; +"lng_context_select_msg_bulk" = "Select up to this message"; "lng_context_report_msg" = "Report Message"; "lng_context_pin_msg" = "Pin Message"; "lng_context_unpin_msg" = "Unpin Message"; @@ -3274,6 +3451,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_mediaview_forward" = "Forward"; "lng_mediaview_delete" = "Delete"; "lng_mediaview_save_to_profile" = "Save to Profile"; +"lng_mediaview_pin_story_done" = "Story pinned"; +"lng_mediaview_pin_story_about" = "Now it will be always shown on the top."; +"lng_mediaview_pin_stories_done#one" = "{count} story pinned"; +"lng_mediaview_pin_stories_done#other" = "{count} stories pinned"; +"lng_mediaview_pin_stories_about#one" = "Now it will be always shown on the top."; +"lng_mediaview_pin_stories_about#other" = "Now they will be always shown on the top."; +"lng_mediaview_unpin_story_done" = "Story unpinned."; +"lng_mediaview_unpin_stories_done#one" = "{count} story unpinned"; +"lng_mediaview_unpin_stories_done#other" = "{count} stories unpinned"; +"lng_mediaview_pin_limit#one" = "You can't pin more than {count} story."; +"lng_mediaview_pin_limit#other" = "You can't pin more than {count} stories."; "lng_mediaview_archive_story" = "Archive Story"; "lng_mediaview_photos_all" = "View all photos"; "lng_mediaview_files_all" = "View all files"; @@ -3512,6 +3700,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_call_cancel" = "Cancel"; "lng_call_microphone_off" = "{user}'s microphone is off"; +"lng_call_battery_level_low" = "{user}'s battery level is low"; "lng_group_call_title" = "Voice Chat"; "lng_group_call_title_channel" = "Live Stream"; @@ -4348,10 +4537,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_translate_settings_about" = "The 'Translate' button will appear when you open a context menu on a text message."; "lng_translate_settings_one" = "Please choose at least one language so that it can be used as the \"Translate to\" language."; -"lng_launch_exe_warning" = "This file has a {extension} extension.\nAre you sure you want to run it?"; +"lng_launch_exe_warning" = "This file has {extension} extension.\nAre you sure you want to run it?"; +"lng_launch_other_warning" = "This file has {extension} extension.\nAre you sure you want to open it?"; "lng_launch_svg_warning" = "Opening this file can potentially expose your IP address to its sender. Continue?"; "lng_launch_exe_sure" = "Run"; +"lng_launch_other_sure" = "Open"; "lng_launch_exe_dont_ask" = "Don't ask me again"; +"lng_launch_dont_ask" = "Remember for this file type"; +"lng_launch_dont_ask_settings" = "You can later edit trusted file types in Settings > Privacy and Security > File open confirmations."; "lng_polls_anonymous" = "Anonymous Poll"; "lng_polls_public" = "Poll"; @@ -4563,6 +4756,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_view_button_boost" = "Boost"; "lng_view_button_giftcode" = "Open"; "lng_view_button_iv" = "Instant View"; +"lng_view_button_stickerset" = "View stickers"; +"lng_view_button_emojipack" = "View emoji"; "lng_sponsored_hide_ads" = "Hide"; "lng_sponsored_title" = "What are sponsored messages?"; @@ -4570,6 +4765,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_sponsored_info_description2" = "Sponsored Messages are currently in test mode. Once they are fully launched and allow Telegram to cover its basic costs, we will start sharing ad revenue with the owners of public channels in which sponsored messages are displayed.\n\nOnline ads should no longer be synonymous with abuse of user privacy. Let us redefine how a tech company should operate – together."; "lng_sponsored_info_menu" = "About this ad"; "lng_sponsored_info_submenu" = "Advertiser: {text}"; +"lng_sponsored_menu_revenued_about" = "About These Ads"; +"lng_sponsored_menu_revenued_report" = "Report Ad"; +"lng_sponsored_revenued_subtitle" = "Telegram Ads are very different from ads on other platforms. Ads such as this one:"; +"lng_sponsored_revenued_info1_title" = "Respect Your Privacy"; +"lng_sponsored_revenued_info1_description" = "Ads on Telegram do not use your personal information and are abased on the channel in which you see them."; +"lng_sponsored_revenued_info2_title" = "Help the Channel Creator"; +"lng_sponsored_revenued_info2_description" = "50% of the revenue from Telegram Ads goes to the owner of the channel where they are displayed."; +"lng_sponsored_revenued_info3_title" = "Can Be Removed"; +"lng_sponsored_revenued_info3_description#one" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_info3_description#other" = "You can turn off ads by subscribing to {link}, and Level {count} channels can remove them for their subscribers."; +"lng_sponsored_revenued_footer_title" = "Can I Launch an Ad?"; +"lng_sponsored_revenued_footer_description" = "Anyone can create an ad to display in this channel — with minimal budgets. Check out the **Telegram Ad Platform** for details. {link}"; "lng_telegram_features_url" = "https://t.me/TelegramTips"; @@ -4865,11 +5072,101 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_boosts_prepaid_giveaway_status#one" = "{count} subscription {duration}"; "lng_boosts_prepaid_giveaway_status#other" = "{count} subscriptions {duration}"; +"lng_channel_earn_title" = "Monetization"; +"lng_channel_earn_about" = "Telegram shares 50% of the revenue from ads displayed in your channel. {link}"; +"lng_channel_earn_about_link" = "Learn more {emoji}"; +"lng_channel_earn_overview_title" = "Proceeds overview"; +"lng_channel_earn_available" = "Balance available to withdraw"; +"lng_channel_earn_reward" = "Proceeds since last withdrawal"; +"lng_channel_earn_total" = "Total lifetime proceeds"; +"lng_channel_earn_balance_title" = "Available balance"; +"lng_channel_earn_balance_button" = "Withdraw"; +"lng_channel_earn_balance_password_title" = "Two-step verification"; +"lng_channel_earn_balance_password_description" = "Please enter your password to withdraw."; +"lng_channel_earn_balance_about" = "Collect your reward using Fragment, a third-party platform used by the advertiser to pay for the ad. {link}"; +"lng_channel_earn_balance_about_temp" = "In the coming weeks you will be able to collect your reward using Fragment, a third-party platform used by advertisers to pay ads. {link}"; +"lng_channel_earn_transfer_sure_about1" = "Check the address of the recipient:"; +"lng_channel_earn_transfer_sure_about2" = "This action can not be undone. If the address above is incorrect you will lose your TON."; +"lng_channel_earn_history_title" = "Transaction history"; +"lng_channel_earn_history_in" = "Proceeds from ads"; +"lng_channel_earn_history_in_about" = "Proceeds from ads displayed in"; +"lng_channel_earn_history_out" = "Withdrawal"; +"lng_channel_earn_history_out_failed" = "Not Completed"; +"lng_channel_earn_history_out_about_failed" = "Withdrawal failed"; +"lng_channel_earn_history_out_button" = "View in Blockchain Explorer"; +"lng_channel_earn_history_return" = "Refund"; +"lng_channel_earn_history_return_about" = "Refunded back"; +"lng_channel_earn_history_pending" = "Pending"; +"lng_channel_earn_history_show_more#one" = "Show {count} More Transaction"; +"lng_channel_earn_history_show_more#other" = "Show {count} More Transactions"; +"lng_channel_earn_off" = "Switch Off Ads"; +"lng_channel_earn_off_about" = "You will not be eligible for any rewards if you switch off ads."; +"lng_channel_earn_cpm_min" = "No Ads"; +"lng_channel_earn_cpm#one" = "{emoji} {count} CPM"; +"lng_channel_earn_cpm#other" = "{emoji} {count} CPM"; +"lng_channel_earn_learn_title" = "Earn From Your Channel"; +"lng_channel_earn_learn_in_subtitle" = "Telegram Ads"; +"lng_channel_earn_learn_in_about" = "Telegram can display ads in your channel."; +"lng_channel_earn_learn_split_subtitle" = "50:50 revenue split"; +"lng_channel_earn_learn_split_about" = "You can receive 50% of the ad revenue in TON."; +"lng_channel_earn_learn_out_subtitle" = "Flexible withdrawals"; +"lng_channel_earn_learn_out_about" = "You can withdraw your TON any time."; +"lng_channel_earn_learn_coin_title" = "What is {emoji} TON?"; +"lng_channel_earn_learn_coin_about" = "TON is a blockchain platform and cryptocurrency that Telegram uses for its high speed and low commissions on transactions. {link}"; +"lng_channel_earn_learn_close" = "Got it"; +"lng_channel_earn_learn_coin_link" = "https://telegram.org/blog/monetization-for-channels"; +"lng_channel_earn_chart_top_hours" = "Ad impressions"; +"lng_channel_earn_chart_revenue" = "Ad revenue"; +"lng_channel_earn_chart_overriden_detail_currency" = "Revenue in TON"; +"lng_channel_earn_chart_overriden_detail_usd" = "Revenue in USD"; + "lng_contact_add" = "Add"; "lng_contact_send_message" = "message"; "lng_iv_open_in_browser" = "Open in Browser"; "lng_iv_share" = "Share"; +"lng_iv_join_channel" = "Join"; +"lng_iv_window_title" = "Instant View"; +"lng_iv_wrong_layout" = "Wrong layout?"; + +"lng_limit_download_title" = "Download speed limited"; +"lng_limit_download_subscribe" = "Subscribe to {link} and increase download speed {increase}."; +"lng_limit_download_increase_times#one" = "**{count}** time"; +"lng_limit_download_increase_times#other" = "**{count}** times"; +"lng_limit_download_increase_speed" = "by **{percent}**"; +"lng_limit_download_subscribe_link" = "Telegram Premium"; +"lng_limit_upload_title" = "Upload speed limited"; +"lng_limit_upload_subscribe" = "Subscribe to {link} and increase upload speed {increase}."; +"lng_limit_upload_increase_times#one" = "**{count}** time"; +"lng_limit_upload_increase_times#other" = "**{count}** times"; +"lng_limit_upload_increase_speed" = "by **{percent}**"; +"lng_limit_upload_subscribe_link" = "Telegram Premium"; + +"lng_recent_frequent" = "Frequent contacts"; +"lng_recent_frequent_all" = "Show all"; +"lng_recent_frequent_collapse" = "Collapse"; +"lng_recent_title" = "Recent"; +"lng_recent_clear" = "Clear"; +"lng_recent_clear_sure" = "Do you want to clear your search history?"; +"lng_recent_remove" = "Remove from Recent"; +"lng_recent_clear_all" = "Clear all"; +"lng_recent_hide_top" = "Remove all & Disable"; +"lng_recent_hide_sure" = "Are you sure you want to clear and disable frequent contacts list?\n\nYou can always turn this feature back on in Settings > Privacy > Suggest Frequent Contacts."; +"lng_recent_hide_button" = "Hide"; +"lng_recent_none" = "Recent search results\nwill appear here."; +"lng_recent_chats" = "Chats"; +"lng_recent_channels" = "Channels"; +"lng_channels_none_title" = "No channels yet..."; +"lng_channels_none_about" = "You are not currently subscribed to any channels."; +"lng_channels_your_title" = "Channels you joined"; +"lng_channels_your_more" = "Show more"; +"lng_channels_your_less" = "Show less"; +"lng_channels_recommended" = "Recommended channels"; + +"lng_font_box_title" = "Choose font family"; +"lng_font_default" = "Default"; +"lng_font_system" = "System font"; +"lng_font_not_found" = "Font not found."; // Wnd specific diff --git a/Telegram/Resources/langs/rewrites/en.json b/Telegram/Resources/langs/rewrites/en.json index 272127303..233e1f0dc 100644 --- a/Telegram/Resources/langs/rewrites/en.json +++ b/Telegram/Resources/langs/rewrites/en.json @@ -44,21 +44,10 @@ "ktg_settings_fonts": "Change application fonts", "ktg_fonts_reset": "Reset", "ktg_fonts_about": "You will need to restart app to apply and see changes.", - "ktg_fonts_main": "Main font", - "ktg_fonts_semibold": "Semibold font", "ktg_fonts_semibold_is_bold": "Bold font face", "ktg_fonts_monospaced": "Monospaced font", "ktg_fonts_size": "Font size: {pixels}px", - "ktg_fonts_use_system_font": "Use system font", - "ktg_fonts_use_original_metrics": "Use Open Sans height", "ktg_settings_network": "Network", - "ktg_settings_net_speed_boost": "Upload speed boost", - "ktg_net_speed_boost_title": "Upload speed boost", - "ktg_net_speed_boost_desc": "Warning: changing this parameter to high values on slow networks can make even worse. Use at your own risk.\n\nYou'll need to restart app to save changes.", - "ktg_net_speed_boost_default": "Disabled", - "ktg_net_speed_boost_slight": "Slight", - "ktg_net_speed_boost_medium": "Medium", - "ktg_net_speed_boost_big": "Big", "ktg_settings_system": "System", "ktg_settings_other": "Other", "ktg_profile_copy_id": "Copy ID", diff --git a/Telegram/Resources/qrc/telegram/animations.qrc b/Telegram/Resources/qrc/telegram/animations.qrc index 12666b6fd..16920ae4d 100644 --- a/Telegram/Resources/qrc/telegram/animations.qrc +++ b/Telegram/Resources/qrc/telegram/animations.qrc @@ -21,5 +21,10 @@ ../../animations/writing.tgs ../../animations/hours.tgs ../../animations/phone.tgs + ../../animations/chat_link.tgs + ../../animations/collectible_username.tgs + ../../animations/collectible_phone.tgs + ../../animations/search.tgs + ../../animations/noresults.tgs diff --git a/Telegram/Resources/uwp/AppX/AppxManifest.xml b/Telegram/Resources/uwp/AppX/AppxManifest.xml index 466034289..cc2453f6b 100644 --- a/Telegram/Resources/uwp/AppX/AppxManifest.xml +++ b/Telegram/Resources/uwp/AppX/AppxManifest.xml @@ -10,7 +10,7 @@ + Version="5.0.6.0" /> Telegram Desktop Telegram Messenger LLP diff --git a/Telegram/Resources/winrc/Telegram.rc b/Telegram/Resources/winrc/Telegram.rc index fda1d34da..0a0a61d84 100644 --- a/Telegram/Resources/winrc/Telegram.rc +++ b/Telegram/Resources/winrc/Telegram.rc @@ -44,8 +44,8 @@ IDI_ICON1 ICON "..\\art\\icon256.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,15,3,0 - PRODUCTVERSION 4,15,3,0 + FILEVERSION 5,0,6,0 + PRODUCTVERSION 5,0,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -62,10 +62,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop" - VALUE "FileVersion", "4.15.3.0" + VALUE "FileVersion", "5.0.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.15.3.0" + VALUE "ProductVersion", "5.0.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/Resources/winrc/Updater.rc b/Telegram/Resources/winrc/Updater.rc index 8ebf2c68b..1b7249bf0 100644 --- a/Telegram/Resources/winrc/Updater.rc +++ b/Telegram/Resources/winrc/Updater.rc @@ -35,8 +35,8 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US // VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,15,3,0 - PRODUCTVERSION 4,15,3,0 + FILEVERSION 5,0,6,0 + PRODUCTVERSION 5,0,6,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -53,10 +53,10 @@ BEGIN BEGIN VALUE "CompanyName", "Telegram FZ-LLC" VALUE "FileDescription", "Telegram Desktop Updater" - VALUE "FileVersion", "4.15.3.0" + VALUE "FileVersion", "5.0.6.0" VALUE "LegalCopyright", "Copyright (C) 2014-2024" VALUE "ProductName", "Telegram Desktop" - VALUE "ProductVersion", "4.15.3.0" + VALUE "ProductVersion", "5.0.6.0" END END BLOCK "VarFileInfo" diff --git a/Telegram/SourceFiles/api/api_bot.cpp b/Telegram/SourceFiles/api/api_bot.cpp index e5071007c..496d8aaf9 100644 --- a/Telegram/SourceFiles/api/api_bot.cpp +++ b/Telegram/SourceFiles/api/api_bot.cpp @@ -238,14 +238,14 @@ void SendBotCallbackDataWithPassword( } else { return; } - const auto box = std::make_shared>(); auto fields = PasscodeBox::CloudFields::From(state); fields.customTitle = tr::lng_bots_password_confirm_title(); fields.customDescription = tr::lng_bots_password_confirm_description(tr::now); fields.customSubmitButton = tr::lng_passcode_submit(); fields.customCheckCallback = [=]( - const Core::CloudPasswordResult &result) { + const Core::CloudPasswordResult &result, + QPointer box) { if (const auto button = getButton()) { if (button->requestId) { return; @@ -259,18 +259,17 @@ void SendBotCallbackDataWithPassword( return; } SendBotCallbackData(strongController, item, row, column, result, [=] { - if (*box) { - (*box)->closeBox(); + if (box) { + box->closeBox(); } }, [=](const QString &error) { - if (*box) { - (*box)->handleCustomCheckError(error); + if (box) { + box->handleCustomCheckError(error); } }); } }; auto object = Box(session, fields); - *box = Ui::MakeWeak(object.data()); show->showBox(std::move(object), Ui::LayerOption::CloseOther); }, *lifetime); } @@ -423,10 +422,10 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) { MTP_int(itemId), MTP_int(id), MTP_vector_from_range( - result - | ranges::views::transform([]( - not_null peer) { - return MTPInputPeer(peer->input); })) + result | ranges::views::transform([]( + not_null peer) { + return MTPInputPeer(peer->input); + })) )).done([=](const MTPUpdates &result) { peer->session().api().applyUpdates(result); }).send(); diff --git a/Telegram/SourceFiles/api/api_chat_links.cpp b/Telegram/SourceFiles/api/api_chat_links.cpp new file mode 100644 index 000000000..aa5be9f55 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_links.cpp @@ -0,0 +1,170 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "api/api_chat_links.h" + +#include "api/api_text_entities.h" +#include "apiwrap.h" +#include "data/data_session.h" +#include "main/main_session.h" + +namespace Api { +namespace { + +[[nodiscard]] ChatLink FromMTP( + not_null session, + const MTPBusinessChatLink &link) { + const auto &data = link.data(); + return { + .link = qs(data.vlink()), + .title = qs(data.vtitle().value_or_empty()), + .message = { + qs(data.vmessage()), + EntitiesFromMTP( + session, + data.ventities().value_or_empty()) + }, + .clicks = data.vviews().v, + }; +} + +[[nodiscard]] MTPInputBusinessChatLink ToMTP( + not_null session, + const QString &title, + const TextWithEntities &message) { + auto entities = EntitiesToMTP( + session, + message.entities, + ConvertOption::SkipLocal); + using Flag = MTPDinputBusinessChatLink::Flag; + const auto flags = (title.isEmpty() ? Flag() : Flag::f_title) + | (entities.v.isEmpty() ? Flag() : Flag::f_entities); + return MTP_inputBusinessChatLink( + MTP_flags(flags), + MTP_string(message.text), + std::move(entities), + MTP_string(title)); +} + +} // namespace + +ChatLinks::ChatLinks(not_null api) : _api(api) { +} + +void ChatLinks::create( + const QString &title, + const TextWithEntities &message, + Fn done) { + const auto session = &_api->session(); + _api->request(MTPaccount_CreateBusinessChatLink( + ToMTP(session, title, message) + )).done([=](const MTPBusinessChatLink &result) { + const auto link = FromMTP(session, result); + _list.push_back(link); + _updates.fire({ .was = QString(), .now = link }); + if (done) done(link); + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(Link()); + }).send(); +} + +void ChatLinks::edit( + const QString &link, + const QString &title, + const TextWithEntities &message, + Fn done) { + const auto session = &_api->session(); + _api->request(MTPaccount_EditBusinessChatLink( + MTP_string(link), + ToMTP(session, title, message) + )).done([=](const MTPBusinessChatLink &result) { + const auto parsed = FromMTP(session, result); + if (parsed.link != link) { + LOG(("API Error: EditBusinessChatLink changed the link.")); + if (done) done(Link()); + return; + } + const auto i = ranges::find(_list, link, &Link::link); + if (i != end(_list)) { + *i = parsed; + _updates.fire({ .was = link, .now = parsed }); + if (done) done(parsed); + } else { + LOG(("API Error: EditBusinessChatLink link not found.")); + if (done) done(Link()); + } + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(Link()); + }).send(); +} + +void ChatLinks::destroy( + const QString &link, + Fn done) { + _api->request(MTPaccount_DeleteBusinessChatLink( + MTP_string(link) + )).done([=] { + const auto i = ranges::find(_list, link, &Link::link); + if (i != end(_list)) { + _list.erase(i); + _updates.fire({ .was = link }); + if (done) done(); + } else { + LOG(("API Error: DeleteBusinessChatLink link not found.")); + if (done) done(); + } + }).fail([=](const MTP::Error &error) { + const auto type = error.type(); + if (done) done(); + }).send(); +} + +void ChatLinks::preload() { + if (_loaded || _requestId) { + return; + } + _requestId = _api->request(MTPaccount_GetBusinessChatLinks( + )).done([=](const MTPaccount_BusinessChatLinks &result) { + const auto &data = result.data(); + const auto session = &_api->session(); + const auto owner = &session->data(); + owner->processUsers(data.vusers()); + owner->processChats(data.vchats()); + auto links = std::vector(); + links.reserve(data.vlinks().v.size()); + for (const auto &link : data.vlinks().v) { + links.push_back(FromMTP(session, link)); + } + _list = std::move(links); + _loaded = true; + _loadedUpdates.fire({}); + }).fail([=] { + _requestId = 0; + _loaded = true; + _loadedUpdates.fire({}); + }).send(); +} + +const std::vector &ChatLinks::list() const { + return _list; +} + +bool ChatLinks::loaded() const { + return _loaded; +} + +rpl::producer<> ChatLinks::loadedUpdates() const { + return _loadedUpdates.events(); +} + +rpl::producer ChatLinks::updates() const { + return _updates.events(); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_links.h b/Telegram/SourceFiles/api/api_chat_links.h new file mode 100644 index 000000000..34226eab9 --- /dev/null +++ b/Telegram/SourceFiles/api/api_chat_links.h @@ -0,0 +1,64 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class ApiWrap; + +namespace Api { + +struct ChatLink { + QString link; + QString title; + TextWithEntities message; + int clicks = 0; +}; + +struct ChatLinkUpdate { + QString was; + std::optional now; +}; + +class ChatLinks final { +public: + explicit ChatLinks(not_null api); + + using Link = ChatLink; + using Update = ChatLinkUpdate; + + void create( + const QString &title, + const TextWithEntities &message, + Fn done = nullptr); + void edit( + const QString &link, + const QString &title, + const TextWithEntities &message, + Fn done = nullptr); + void destroy( + const QString &link, + Fn done = nullptr); + + void preload(); + [[nodiscard]] const std::vector &list() const; + [[nodiscard]] bool loaded() const; + [[nodiscard]] rpl::producer<> loadedUpdates() const; + [[nodiscard]] rpl::producer updates() const; + +private: + const not_null _api; + + std::vector _list; + rpl::event_stream<> _loadedUpdates; + mtpRequestId _requestId = 0; + bool _loaded = false; + + rpl::event_stream _updates; + +}; + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_participants.cpp b/Telegram/SourceFiles/api/api_chat_participants.cpp index 1714d9e2b..a4cd0c367 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.cpp +++ b/Telegram/SourceFiles/api/api_chat_participants.cpp @@ -112,8 +112,8 @@ void ApplyLastList( channel->mgInfo->lastAdmins.clear(); channel->mgInfo->lastRestricted.clear(); channel->mgInfo->lastParticipants.clear(); - channel->mgInfo->lastParticipantsStatus = - MegagroupInfo::LastParticipantsUpToDate + channel->mgInfo->lastParticipantsStatus + = MegagroupInfo::LastParticipantsUpToDate | MegagroupInfo::LastParticipantsOnceReceived; auto botStatus = channel->mgInfo->botStatus; @@ -212,7 +212,7 @@ void ApplyBotsList( } [[nodiscard]] ChatParticipants::Channels ParseSimilar( - not_null channel, + not_null session, const MTPmessages_Chats &chats) { auto result = ChatParticipants::Channels(); std::vector>(); @@ -220,13 +220,13 @@ void ApplyBotsList( const auto &list = data.vchats().v; result.list.reserve(list.size()); for (const auto &chat : list) { - const auto peer = channel->owner().processChat(chat); + const auto peer = session->data().processChat(chat); if (const auto channel = peer->asChannel()) { result.list.push_back(channel); } } if constexpr (MTPDmessages_chatsSlice::Is()) { - if (channel->session().premiumPossible()) { + if (session->premiumPossible()) { result.more = data.vcount().v - data.vchats().v.size(); } } @@ -234,6 +234,12 @@ void ApplyBotsList( return result; } +[[nodiscard]] ChatParticipants::Channels ParseSimilar( + not_null channel, + const MTPmessages_Chats &chats) { + return ParseSimilar(&channel->session(), chats); +} + } // namespace ChatParticipant::ChatParticipant( @@ -351,7 +357,8 @@ QString ChatParticipant::rank() const { } ChatParticipants::ChatParticipants(not_null api) -: _api(&api->instance()) { +: _session(&api->session()) +, _api(&api->instance()) { } void ChatParticipants::requestForAdd( @@ -487,9 +494,9 @@ void ChatParticipants::requestCountDelayed( } void ChatParticipants::add( + std::shared_ptr show, not_null peer, const std::vector> &users, - std::shared_ptr show, bool passGroupHistory, Fn done) { if (const auto chat = peer->asChat()) { @@ -498,19 +505,28 @@ void ChatParticipants::add( chat->inputChat, user->inputUser, MTP_int(passGroupHistory ? kForwardMessagesOnAdd : 0) - )).done([=](const MTPUpdates &result) { - chat->session().api().applyUpdates(result); + )).done([=](const MTPmessages_InvitedUsers &result) { + const auto &data = result.data(); + chat->session().api().applyUpdates(data.vupdates()); if (done) done(true); + ChatInviteForbidden( + show, + chat, + CollectForbiddenUsers(&chat->session(), result)); }).fail([=](const MTP::Error &error) { const auto type = error.type(); - ShowAddParticipantsError(type, peer, { 1, user }, show); + ShowAddParticipantsError(show, type, peer, user); if (done) done(false); }).afterDelay(kSmallDelayMs).send(); } } else if (const auto channel = peer->asChannel()) { const auto hasBot = ranges::any_of(users, &UserData::isBot); if (!peer->isMegagroup() && hasBot) { - ShowAddParticipantsError("USER_BOT", peer, users, show); + ShowAddParticipantsError( + show, + u"USER_BOT"_q, + peer, + { .users = users }); return; } auto list = QVector(); @@ -520,8 +536,9 @@ void ChatParticipants::add( _api.request(MTPchannels_InviteToChannel( channel->inputChannel, MTP_vector(list) - )).done([=](const MTPUpdates &result) { - channel->session().api().applyUpdates(result); + )).done([=](const MTPmessages_InvitedUsers &result) { + const auto &data = result.data(); + channel->session().api().applyUpdates(data.vupdates()); requestCountDelayed(channel); if (callback) callback(true); ChatInviteForbidden( @@ -529,7 +546,9 @@ void ChatParticipants::add( channel, CollectForbiddenUsers(&channel->session(), result)); }).fail([=](const MTP::Error &error) { - ShowAddParticipantsError(error.type(), peer, users, show); + ShowAddParticipantsError(show, error.type(), peer, { + .users = users, + }); if (callback) callback(false); }).afterDelay(kSmallDelayMs).send(); }; @@ -573,6 +592,33 @@ ChatParticipants::Parsed ChatParticipants::ParseRecent( return result; } +void ChatParticipants::Restrict( + not_null channel, + not_null participant, + ChatRestrictionsInfo oldRights, + ChatRestrictionsInfo newRights, + Fn onDone, + Fn onFail) { + channel->session().api().request(MTPchannels_EditBanned( + channel->inputChannel, + participant->input, + MTP_chatBannedRights( + MTP_flags(MTPDchatBannedRights::Flags::from_raw( + uint32(newRights.flags))), + MTP_int(newRights.until)) + )).done([=](const MTPUpdates &result) { + channel->session().api().applyUpdates(result); + channel->applyEditBanned(participant, oldRights, newRights); + if (onDone) { + onDone(); + } + }).fail([=] { + if (onFail) { + onFail(); + } + }).send(); +} + void ChatParticipants::requestSelf(not_null channel) { if (_selfParticipantRequests.contains(channel)) { return; @@ -718,8 +764,11 @@ void ChatParticipants::loadSimilarChannels(not_null channel) { return; } } + using Flag = MTPchannels_GetChannelRecommendations::Flag; _similar[channel].requestId = _api.request( - MTPchannels_GetChannelRecommendations(channel->inputChannel) + MTPchannels_GetChannelRecommendations( + MTP_flags(Flag::f_channel), + channel->inputChannel) ).done([=](const MTPmessages_Chats &result) { auto &similar = _similar[channel]; similar.requestId = 0; @@ -754,4 +803,29 @@ auto ChatParticipants::similarLoaded() const return _similarLoaded.events(); } +void ChatParticipants::loadRecommendations() { + if (_recommendationsLoaded.current() || _recommendations.requestId) { + return; + } + _recommendations.requestId = _api.request( + MTPchannels_GetChannelRecommendations( + MTP_flags(0), + MTP_inputChannelEmpty()) + ).done([=](const MTPmessages_Chats &result) { + _recommendations.requestId = 0; + auto parsed = ParseSimilar(_session, result); + _recommendations.channels = std::move(parsed); + _recommendations.channels.more = 0; + _recommendationsLoaded = true; + }).send(); +} + +const ChatParticipants::Channels &ChatParticipants::recommendations() const { + return _recommendations.channels; +} + +rpl::producer<> ChatParticipants::recommendationsLoaded() const { + return _recommendationsLoaded.changes() | rpl::to_empty; +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_chat_participants.h b/Telegram/SourceFiles/api/api_chat_participants.h index 816cc6526..ad5aa930f 100644 --- a/Telegram/SourceFiles/api/api_chat_participants.h +++ b/Telegram/SourceFiles/api/api_chat_participants.h @@ -14,6 +14,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL class ApiWrap; class ChannelData; +namespace Main { +class Session; +} // namespace Main + namespace Ui { class Show; } // namespace Ui @@ -96,10 +100,17 @@ public: static Parsed ParseRecent( not_null channel, const TLMembers &data); + static void Restrict( + not_null channel, + not_null participant, + ChatRestrictionsInfo oldRights, + ChatRestrictionsInfo newRights, + Fn onDone, + Fn onFail); void add( + std::shared_ptr show, not_null peer, const std::vector> &users, - std::shared_ptr show = nullptr, bool passGroupHistory = true, Fn done = nullptr); @@ -134,12 +145,18 @@ public: [[nodiscard]] auto similarLoaded() const -> rpl::producer>; + void loadRecommendations(); + [[nodiscard]] const Channels &recommendations() const; + [[nodiscard]] rpl::producer<> recommendationsLoaded() const; + private: struct SimilarChannels { Channels channels; mtpRequestId requestId = 0; }; + const not_null _session; + MTP::Sender _api; using PeerRequests = base::flat_map; @@ -165,6 +182,9 @@ private: base::flat_map, SimilarChannels> _similar; rpl::event_stream> _similarLoaded; + SimilarChannels _recommendations; + rpl::variable _recommendationsLoaded = false; + }; } // namespace Api diff --git a/Telegram/SourceFiles/api/api_confirm_phone.cpp b/Telegram/SourceFiles/api/api_confirm_phone.cpp index 0dd1ae9a7..f4788e128 100644 --- a/Telegram/SourceFiles/api/api_confirm_phone.cpp +++ b/Telegram/SourceFiles/api/api_confirm_phone.cpp @@ -62,6 +62,10 @@ void ConfirmPhone::resolve( return bad("FirebaseSms"); }, [&](const MTPDauth_sentCodeTypeEmailCode &) { return bad("EmailCode"); + }, [&](const MTPDauth_sentCodeTypeSmsWord &) { + return bad("SmsWord"); + }, [&](const MTPDauth_sentCodeTypeSmsPhrase &) { + return bad("SmsPhrase"); }, [&](const MTPDauth_sentCodeTypeSetUpEmailRequired &) { return bad("SetUpEmailRequired"); }); diff --git a/Telegram/SourceFiles/api/api_earn.cpp b/Telegram/SourceFiles/api/api_earn.cpp new file mode 100644 index 000000000..d6425ef69 --- /dev/null +++ b/Telegram/SourceFiles/api/api_earn.cpp @@ -0,0 +1,96 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#include "api/api_earn.h" + +#include "api/api_cloud_password.h" +#include "apiwrap.h" +#include "boxes/passcode_box.h" +#include "data/data_channel.h" +#include "data/data_session.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "ui/basic_click_handlers.h" +#include "ui/widgets/buttons.h" + +namespace Api { + +void RestrictSponsored( + not_null channel, + bool restricted, + Fn failed) { + channel->session().api().request(MTPchannels_RestrictSponsoredMessages( + channel->inputChannel, + MTP_bool(restricted)) + ).done([=](const MTPUpdates &updates) { + channel->session().api().applyUpdates(updates); + }).fail([=](const MTP::Error &error) { + failed(error.type()); + }).send(); +} + +void HandleWithdrawalButton( + not_null channel, + not_null button, + std::shared_ptr show) { + struct State { + rpl::lifetime lifetime; + bool loading = false; + }; + + const auto state = button->lifetime().make_state(); + const auto session = &channel->session(); + + session->api().cloudPassword().reload(); + button->setClickedCallback([=] { + if (state->loading) { + return; + } + state->loading = true; + state->lifetime = session->api().cloudPassword().state( + ) | rpl::take( + 1 + ) | rpl::start_with_next([=](const Core::CloudPasswordState &pass) { + state->loading = false; + + auto fields = PasscodeBox::CloudFields::From(pass); + fields.customTitle + = tr::lng_channel_earn_balance_password_title(); + fields.customDescription + = tr::lng_channel_earn_balance_password_description(tr::now); + fields.customSubmitButton = tr::lng_passcode_submit(); + fields.customCheckCallback = crl::guard(button, [=]( + const Core::CloudPasswordResult &result, + QPointer box) { + const auto done = [=](const QString &result) { + if (!result.isEmpty()) { + UrlClickHandler::Open(result); + if (box) { + box->closeBox(); + } + } + }; + const auto fail = [=](const QString &error) { + show->showToast(error); + }; + session->api().request( + MTPstats_GetBroadcastRevenueWithdrawalUrl( + channel->inputChannel, + result.result + )).done([=](const MTPstats_BroadcastRevenueWithdrawalUrl &r) { + done(qs(r.data().vurl())); + }).fail([=](const MTP::Error &error) { + fail(error.type()); + }).send(); + }); + show->show(Box(session, fields)); + }); + + }); +} + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_earn.h b/Telegram/SourceFiles/api/api_earn.h new file mode 100644 index 000000000..93f2bf6eb --- /dev/null +++ b/Telegram/SourceFiles/api/api_earn.h @@ -0,0 +1,29 @@ +/* +This file is part of Telegram Desktop, +the official desktop application for the Telegram messaging service. + +For license and copyright information please follow this link: +https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL +*/ +#pragma once + +class ChannelData; + +namespace Ui { +class RippleButton; +class Show; +} // namespace Ui + +namespace Api { + +void RestrictSponsored( + not_null channel, + bool restricted, + Fn failed); + +void HandleWithdrawalButton( + not_null channel, + not_null button, + std::shared_ptr show); + +} // namespace Api diff --git a/Telegram/SourceFiles/api/api_editing.cpp b/Telegram/SourceFiles/api/api_editing.cpp index 84f0cbfff..7dcd768cb 100644 --- a/Telegram/SourceFiles/api/api_editing.cpp +++ b/Telegram/SourceFiles/api/api_editing.cpp @@ -12,12 +12,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_text_entities.h" #include "ui/boxes/confirm_box.h" #include "data/business/data_shortcut_messages.h" +#include "data/components/scheduled_messages.h" +#include "data/data_file_origin.h" #include "data/data_histories.h" -#include "data/data_scheduled_messages.h" #include "data/data_session.h" #include "data/data_web_page.h" #include "history/history.h" -#include "history/history_item.h" #include "lang/lang_keys.h" #include "main/main_session.h" #include "mtproto/mtproto_response.h" @@ -29,20 +29,20 @@ namespace { using namespace rpl::details; template -constexpr auto WithId = - is_callable_plain_v, mtpRequestId>; +constexpr auto WithId + = is_callable_plain_v, mtpRequestId>; template -constexpr auto WithoutId = - is_callable_plain_v>; +constexpr auto WithoutId + = is_callable_plain_v>; template -constexpr auto WithoutCallback = - is_callable_plain_v; +constexpr auto WithoutCallback + = is_callable_plain_v; template -constexpr auto ErrorWithId = - is_callable_plain_v; +constexpr auto ErrorWithId + = is_callable_plain_v; template -constexpr auto ErrorWithoutId = - is_callable_plain_v; +constexpr auto ErrorWithoutId + = is_callable_plain_v; template mtpRequestId EditMessage( @@ -95,7 +95,7 @@ mtpRequestId EditMessage( : emptyFlag); const auto id = item->isScheduled() - ? session->data().scheduledMessages().lookupId(item) + ? session->scheduledMessages().lookupId(item) : item->isBusinessShortcut() ? session->data().shortcutMessages().lookupId(item) : item->id; @@ -253,12 +253,100 @@ mtpRequestId EditTextMessage( Data::WebPageDraft webpage, SendOptions options, Fn done, - Fn fail) { + Fn fail, + std::optional spoilerMediaOverride) { + if (spoilerMediaOverride) { + const auto spoiler = *spoilerMediaOverride; + if (const auto media = item->media()) { + auto takeInputMedia = Fn()>(nullptr); + auto takeFileReference = Fn(nullptr); + if (const auto photo = media->photo()) { + using Flag = MTPDinputMediaPhoto::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoiler ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaPhoto( + MTP_flags(flags), + photo->mtpInput(), + MTP_int(media->ttlSeconds())); + }; + takeFileReference = [=] { return photo->fileReference(); }; + } else if (const auto document = media->document()) { + using Flag = MTPDinputMediaDocument::Flag; + const auto flags = Flag() + | (media->ttlSeconds() ? Flag::f_ttl_seconds : Flag()) + | (spoiler ? Flag::f_spoiler : Flag()); + takeInputMedia = [=] { + return MTP_inputMediaDocument( + MTP_flags(flags), + document->mtpInput(), + MTP_int(media->ttlSeconds()), + MTPstring()); // query + }; + takeFileReference = [=] { return document->fileReference(); }; + } + + const auto usedFileReference = takeFileReference + ? takeFileReference() + : QByteArray(); + const auto origin = item->fullId(); + const auto api = &item->history()->session().api(); + const auto performRequest = [=]( + const auto &repeatRequest, + mtpRequestId originalRequestId) -> mtpRequestId { + const auto handleReference = [=]( + const QString &error, + mtpRequestId requestId) { + if (error.startsWith(u"FILE_REFERENCE_"_q)) { + api->refreshFileReference(origin, [=](const auto &) { + if (takeFileReference && + (takeFileReference() != usedFileReference)) { + repeatRequest( + repeatRequest, + originalRequestId + ? originalRequestId + : requestId); + } else { + fail(error, requestId); + } + }); + } else { + fail(error, requestId); + } + }; + const auto callback = [=]( + Fn applyUpdates, + mtpRequestId requestId) { + applyUpdates(); + done(originalRequestId ? originalRequestId : requestId); + }; + const auto requestId = EditMessage( + item, + caption, + webpage, + options, + callback, + handleReference, + takeInputMedia ? takeInputMedia() : std::nullopt); + return originalRequestId ? originalRequestId : requestId; + }; + return performRequest(performRequest, 0); + } + } + const auto callback = [=](Fn applyUpdates, mtpRequestId id) { applyUpdates(); done(id); }; - return EditMessage(item, caption, webpage, options, callback, fail); + return EditMessage( + item, + caption, + webpage, + options, + callback, + fail, + std::nullopt); } } // namespace Api diff --git a/Telegram/SourceFiles/api/api_editing.h b/Telegram/SourceFiles/api/api_editing.h index 86f106f45..c8d0f6c50 100644 --- a/Telegram/SourceFiles/api/api_editing.h +++ b/Telegram/SourceFiles/api/api_editing.h @@ -55,6 +55,7 @@ mtpRequestId EditTextMessage( Data::WebPageDraft webpage, SendOptions options, Fn done, - Fn fail); + Fn fail, + std::optional spoilerMediaOverride); } // namespace Api diff --git a/Telegram/SourceFiles/api/api_global_privacy.cpp b/Telegram/SourceFiles/api/api_global_privacy.cpp index f958bdfe8..d1beddfb6 100644 --- a/Telegram/SourceFiles/api/api_global_privacy.cpp +++ b/Telegram/SourceFiles/api/api_global_privacy.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" namespace Api { @@ -40,9 +39,9 @@ void GlobalPrivacy::reload(Fn callback) { } }).send(); - _session->account().appConfig().value( + _session->appConfig().value( ) | rpl::start_with_next([=] { - _showArchiveAndMute = _session->account().appConfig().get( + _showArchiveAndMute = _session->appConfig().get( u"autoarchive_setting_available"_q, false); }, _session->lifetime()); @@ -75,12 +74,12 @@ rpl::producer GlobalPrivacy::showArchiveAndMute() const { } rpl::producer<> GlobalPrivacy::suggestArchiveAndMute() const { - return _session->account().appConfig().suggestionRequested( + return _session->appConfig().suggestionRequested( u"AUTOARCHIVE_POPULAR"_q); } void GlobalPrivacy::dismissArchiveAndMuteSuggestion() { - _session->account().appConfig().dismissSuggestion( + _session->appConfig().dismissSuggestion( u"AUTOARCHIVE_POPULAR"_q); } @@ -142,6 +141,8 @@ void GlobalPrivacy::update( using Flag = MTPDglobalPrivacySettings::Flag; _api.request(_requestId).cancel(); + const auto newRequirePremiumAllowed = _session->premium() + || _session->appConfig().newRequirePremiumFree(); const auto flags = Flag() | (archiveAndMute ? Flag::f_archive_and_mute_new_noncontact_peers @@ -153,7 +154,7 @@ void GlobalPrivacy::update( ? Flag::f_keep_archived_folders : Flag()) | (hideReadTime ? Flag::f_hide_read_marks : Flag()) - | ((newRequirePremium && _session->premium()) + | ((newRequirePremium && newRequirePremiumAllowed) ? Flag::f_new_noncontact_peers_require_premium : Flag()); _requestId = _api.request(MTPaccount_SetGlobalPrivacySettings( diff --git a/Telegram/SourceFiles/api/api_peer_photo.cpp b/Telegram/SourceFiles/api/api_peer_photo.cpp index c70ef4491..9c8a470fa 100644 --- a/Telegram/SourceFiles/api/api_peer_photo.cpp +++ b/Telegram/SourceFiles/api/api_peer_photo.cpp @@ -34,7 +34,7 @@ namespace { constexpr auto kSharedMediaLimit = 100; -[[nodiscard]] SendMediaReady PreparePeerPhoto( +[[nodiscard]] std::shared_ptr PreparePeerPhoto( MTP::DcId dcId, PeerId peerId, QImage &&image) { @@ -80,24 +80,17 @@ constexpr auto kSharedMediaLimit = 100; MTPVector(), MTP_int(dcId)); - QString file, filename; - int64 filesize = 0; - QByteArray data; - - return SendMediaReady( - SendMediaType::Photo, - file, - filename, - filesize, - data, - id, - id, - u"jpg"_q, - peerId, - photo, - photoThumbs, - MTP_documentEmpty(MTP_long(0)), - jpeg); + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::Photo, + }); + result->type = SendMediaType::Photo; + result->setFileData(jpeg); + result->thumbId = id; + result->thumbname = "thumb.jpg"; + result->photo = photo; + result->photoThumbs = photoThumbs; + return result; } [[nodiscard]] std::optional PrepareMtpMarkup( @@ -239,7 +232,7 @@ void PeerPhoto::upload( _api.instance().mainDcId(), peer->id, base::take(photo.image)); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } } diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 72da2a489..6ff4aa130 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -19,7 +19,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_element.h" #include "history/history.h" #include "history/history_item.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" #include "payments/payments_form.h" @@ -109,6 +108,18 @@ rpl::producer<> Premium::cloudSetUpdated() const { return _cloudSetUpdated.events(); } +auto Premium::helloStickers() const +-> const std::vector> & { + if (_helloStickers.empty()) { + const_cast(this)->reloadHelloStickers(); + } + return _helloStickers; +} + +rpl::producer<> Premium::helloStickersUpdated() const { + return _helloStickersUpdated.events(); +} + int64 Premium::monthlyAmount() const { return _monthlyAmount; } @@ -225,6 +236,33 @@ void Premium::reloadCloudSet() { }).send(); } +void Premium::reloadHelloStickers() { + if (_helloStickersRequestId) { + return; + } + _helloStickersRequestId = _api.request(MTPmessages_GetStickers( + MTP_string("\xf0\x9f\x91\x8b\xe2\xad\x90\xef\xb8\x8f"), + MTP_long(_helloStickersHash) + )).done([=](const MTPmessages_Stickers &result) { + _helloStickersRequestId = 0; + result.match([&](const MTPDmessages_stickersNotModified &) { + }, [&](const MTPDmessages_stickers &data) { + _helloStickersHash = data.vhash().v; + const auto owner = &_session->data(); + _helloStickers.clear(); + for (const auto &sticker : data.vstickers().v) { + const auto document = owner->processDocument(sticker); + if (document->sticker()) { + _helloStickers.push_back(document); + } + } + _helloStickersUpdated.fire({}); + }); + }).fail([=] { + _helloStickersRequestId = 0; + }).send(); +} + void Premium::checkGiftCode( const QString &slug, Fn done) { @@ -532,38 +570,73 @@ Data::SubscriptionOptions PremiumGiftCodeOptions::options(int amount) { int PremiumGiftCodeOptions::giveawayBoostsPerPremium() const { constexpr auto kFallbackCount = 4; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_boosts_per_premium"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayCountriesMax() const { constexpr auto kFallbackCount = 10; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_countries_max"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayAddPeersMax() const { constexpr auto kFallbackCount = 10; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_add_peers_max"_q, kFallbackCount); } int PremiumGiftCodeOptions::giveawayPeriodMax() const { constexpr auto kFallbackCount = 3600 * 24 * 7; - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_period_max"_q, kFallbackCount); } bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const { - return _peer->session().account().appConfig().get( + return _peer->session().appConfig().get( u"giveaway_gifts_purchase_available"_q, false); } +SponsoredToggle::SponsoredToggle(not_null session) +: _api(&session->api().instance()) { +} + +rpl::producer SponsoredToggle::toggled() { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + _api.request(MTPusers_GetFullUser( + MTP_inputUserSelf() + )).done([=](const MTPusers_UserFull &result) { + consumer.put_next_copy( + result.data().vfull_user().data().is_sponsored_enabled()); + }).fail([=] { consumer.put_next(false); }).send(); + + return lifetime; + }; +} + +rpl::producer SponsoredToggle::setToggled(bool v) { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + _api.request(MTPaccount_ToggleSponsoredMessages( + MTP_bool(v) + )).done([=] { + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + + return lifetime; + }; +} + RequirePremiumState ResolveRequiresPremiumToWrite( not_null peer, History *maybeHistory) { @@ -609,4 +682,24 @@ RequirePremiumState ResolveRequiresPremiumToWrite( return RequirePremiumState::Unknown; } +rpl::producer RandomHelloStickerValue( + not_null session) { + const auto premium = &session->api().premium(); + const auto random = [=] { + const auto &v = premium->helloStickers(); + Assert(!v.empty()); + return v[base::RandomIndex(v.size())].get(); + }; + const auto &v = premium->helloStickers(); + if (!v.empty()) { + return rpl::single(random()); + } + return rpl::single( + nullptr + ) | rpl::then(premium->helloStickersUpdated( + ) | rpl::filter([=] { + return !premium->helloStickers().empty(); + }) | rpl::take(1) | rpl::map(random)); +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index e1c3a7b41..83423fcd2 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -85,6 +85,10 @@ public: -> const std::vector> &; [[nodiscard]] rpl::producer<> cloudSetUpdated() const; + [[nodiscard]] auto helloStickers() const + -> const std::vector> &; + [[nodiscard]] rpl::producer<> helloStickersUpdated() const; + [[nodiscard]] int64 monthlyAmount() const; [[nodiscard]] QString monthlyCurrency() const; @@ -111,6 +115,7 @@ private: void reloadPromo(); void reloadStickers(); void reloadCloudSet(); + void reloadHelloStickers(); void requestPremiumRequiredSlice(); const not_null _session; @@ -133,6 +138,11 @@ private: std::vector> _cloudSet; rpl::event_stream<> _cloudSetUpdated; + mtpRequestId _helloStickersRequestId = 0; + uint64 _helloStickersHash = 0; + std::vector> _helloStickers; + rpl::event_stream<> _helloStickersUpdated; + int64 _monthlyAmount = 0; QString _monthlyCurrency; @@ -206,6 +216,18 @@ private: }; +class SponsoredToggle final { +public: + explicit SponsoredToggle(not_null session); + + [[nodiscard]] rpl::producer toggled(); + [[nodiscard]] rpl::producer setToggled(bool); + +private: + MTP::Sender _api; + +}; + enum class RequirePremiumState { Unknown, Yes, @@ -215,4 +237,7 @@ enum class RequirePremiumState { not_null peer, History *maybeHistory); +[[nodiscard]] rpl::producer RandomHelloStickerValue( + not_null session); + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_ringtones.cpp b/Telegram/SourceFiles/api/api_ringtones.cpp index 5b471851e..307ba580a 100644 --- a/Telegram/SourceFiles/api/api_ringtones.cpp +++ b/Telegram/SourceFiles/api/api_ringtones.cpp @@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/notify/data_notify_settings.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" #include "storage/file_upload.h" @@ -25,16 +24,25 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Api { namespace { -SendMediaReady PrepareRingtoneDocument( +std::shared_ptr PrepareRingtoneDocument( MTP::DcId dcId, const QString &filename, const QString &filemime, const QByteArray &content) { + const auto id = base::RandomValue(); auto attributes = QVector( 1, MTP_documentAttributeFilename(MTP_string(filename))); - const auto id = base::RandomValue(); - const auto document = MTP_document( + + auto result = MakePreparedFile({ + .id = id, + .type = SendMediaType::File, + }); + result->filename = filename; + result->content = content; + result->filesize = content.size(); + result->setFileData(content); + result->document = MTP_document( MTP_flags(0), MTP_long(id), MTP_long(0), @@ -46,21 +54,7 @@ SendMediaReady PrepareRingtoneDocument( MTPVector(), MTP_int(dcId), MTP_vector(std::move(attributes))); - - return SendMediaReady( - SendMediaType::File, - QString(), // filepath - filename, - content.size(), - content, - id, - 0, - QString(), - PeerId(), - MTP_photoEmpty(MTP_long(0)), - PreparedPhotoThumbs(), - document, - QByteArray()); + return result; } } // namespace @@ -103,7 +97,7 @@ void Ringtones::upload( _uploads.erase(already); } _uploads.emplace(fakeId, uploadedData); - _session->uploader().uploadMedia(fakeId, ready); + _session->uploader().upload(fakeId, ready); } void Ringtones::ready(const FullMsgId &msgId, const MTPInputFile &file) { @@ -191,20 +185,20 @@ void Ringtones::remove(DocumentId id) { } int64 Ringtones::maxSize() const { - return _session->account().appConfig().get( - "ringtone_size_max", + return _session->appConfig().get( + u"ringtone_size_max"_q, 100 * 1024); } int Ringtones::maxSavedCount() const { - return _session->account().appConfig().get( - "ringtone_saved_count_max", + return _session->appConfig().get( + u"ringtone_saved_count_max"_q, 100); } crl::time Ringtones::maxDuration() const { - return crl::time(1000) * _session->account().appConfig().get( - "ringtone_duration_max", + return crl::time(1000) * _session->appConfig().get( + u"ringtone_duration_max"_q, 5); } diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index 3f45cc43d..d2cfe6260 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -29,7 +29,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/text/text_entity.h" // TextWithEntities. #include "ui/item_text_options.h" // Ui::ItemTextOptions. #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "storage/localimageloader.h" #include "storage/file_upload.h" @@ -277,8 +276,7 @@ bool SendDice( || !message.textWithTags.tags.isEmpty()) { return false; } - auto &account = message.action.history->session().account(); - auto &config = account.appConfig(); + auto &config = message.action.history->session().appConfig(); static const auto hardcoded = std::vector{ Stickers::DicePacks::kDiceString, Stickers::DicePacks::kDartString, @@ -395,7 +393,7 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file) { + const std::shared_ptr &file) { const auto isEditing = (file->type != SendMediaType::Audio) && (file->to.replaceMediaOf != 0); const auto newId = FullMsgId( diff --git a/Telegram/SourceFiles/api/api_sending.h b/Telegram/SourceFiles/api/api_sending.h index 674ffca89..463bf2f9b 100644 --- a/Telegram/SourceFiles/api/api_sending.h +++ b/Telegram/SourceFiles/api/api_sending.h @@ -14,7 +14,7 @@ class Session; class History; class PhotoData; class DocumentData; -struct FileLoadResult; +struct FilePrepareResult; namespace MTP { class Error; @@ -62,7 +62,7 @@ void FillMessagePostFlags( void SendConfirmedFile( not_null session, - const std::shared_ptr &file); + const std::shared_ptr &file); void SendLocationPoint( const Data::LocationPoint &data, diff --git a/Telegram/SourceFiles/api/api_sensitive_content.cpp b/Telegram/SourceFiles/api/api_sensitive_content.cpp index e2d3e49ed..97388d638 100644 --- a/Telegram/SourceFiles/api/api_sensitive_content.cpp +++ b/Telegram/SourceFiles/api/api_sensitive_content.cpp @@ -9,7 +9,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "main/main_session.h" -#include "main/main_account.h" #include "main/main_app_config.h" namespace Api { @@ -22,7 +21,7 @@ constexpr auto kRefreshAppConfigTimeout = 3 * crl::time(1000); SensitiveContent::SensitiveContent(not_null api) : _session(&api->session()) , _api(&api->instance()) -, _appConfigReloadTimer([=] { _session->account().appConfig().refresh(); }) { +, _appConfigReloadTimer([=] { _session->appConfig().refresh(); }) { } void SensitiveContent::reload() { diff --git a/Telegram/SourceFiles/api/api_statistics.cpp b/Telegram/SourceFiles/api/api_statistics.cpp index 789211f33..ce1966623 100644 --- a/Telegram/SourceFiles/api/api_statistics.cpp +++ b/Telegram/SourceFiles/api/api_statistics.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_statistics.h" #include "apiwrap.h" +#include "base/unixtime.h" #include "data/data_channel.h" #include "data/data_session.h" #include "data/data_stories.h" @@ -746,4 +747,126 @@ Data::BoostStatus Boosts::boostStatus() const { return _boostStatus; } +EarnStatistics::EarnStatistics(not_null channel) +: StatisticsRequestSender(channel) { +} + +rpl::producer EarnStatistics::request() { + return [=](auto consumer) { + auto lifetime = rpl::lifetime(); + + makeRequest(MTPstats_GetBroadcastRevenueStats( + MTP_flags(0), + channel()->inputChannel + )).done([=](const MTPstats_BroadcastRevenueStats &result) { + const auto &data = result.data(); + const auto &balances = data.vbalances().data(); + _data = Data::EarnStatistics{ + .topHoursGraph = StatisticalGraphFromTL( + data.vtop_hours_graph()), + .revenueGraph = StatisticalGraphFromTL(data.vrevenue_graph()), + .currentBalance = balances.vcurrent_balance().v, + .availableBalance = balances.vavailable_balance().v, + .overallRevenue = balances.voverall_revenue().v, + .usdRate = data.vusd_rate().v, + }; + + requestHistory({}, [=](Data::EarnHistorySlice &&slice) { + _data.firstHistorySlice = std::move(slice); + + api().request( + MTPchannels_GetFullChannel(channel()->inputChannel) + ).done([=](const MTPmessages_ChatFull &result) { + result.data().vfull_chat().match([&]( + const MTPDchannelFull &d) { + _data.switchedOff = d.is_restricted_sponsored(); + }, [](const auto &) { + }); + consumer.put_done(); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + }); + }).fail([=](const MTP::Error &error) { + consumer.put_error_copy(error.type()); + }).send(); + + return lifetime; + }; +} + +void EarnStatistics::requestHistory( + const Data::EarnHistorySlice::OffsetToken &token, + Fn done) { + if (_requestId) { + return; + } + constexpr auto kTlFirstSlice = tl::make_int(kFirstSlice); + constexpr auto kTlLimit = tl::make_int(kLimit); + _requestId = api().request(MTPstats_GetBroadcastRevenueTransactions( + channel()->inputChannel, + MTP_int(token), + (!token) ? kTlFirstSlice : kTlLimit + )).done([=](const MTPstats_BroadcastRevenueTransactions &result) { + _requestId = 0; + + const auto &tlTransactions = result.data().vtransactions().v; + + auto list = std::vector(); + list.reserve(tlTransactions.size()); + for (const auto &tlTransaction : tlTransactions) { + list.push_back(tlTransaction.match([&]( + const MTPDbroadcastRevenueTransactionProceeds &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::In, + .amount = d.vamount().v, + .date = base::unixtime::parse(d.vfrom_date().v), + .dateTo = base::unixtime::parse(d.vto_date().v), + }; + }, [&](const MTPDbroadcastRevenueTransactionWithdrawal &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::Out, + .status = d.is_pending() + ? Data::EarnHistoryEntry::Status::Pending + : d.is_failed() + ? Data::EarnHistoryEntry::Status::Failed + : Data::EarnHistoryEntry::Status::Success, + .amount = (std::numeric_limits::max() + - d.vamount().v + + 1), + .date = base::unixtime::parse(d.vdate().v), + // .provider = qs(d.vprovider()), + .successDate = d.vtransaction_date() + ? base::unixtime::parse(d.vtransaction_date()->v) + : QDateTime(), + .successLink = d.vtransaction_url() + ? qs(*d.vtransaction_url()) + : QString(), + }; + }, [&](const MTPDbroadcastRevenueTransactionRefund &d) { + return Data::EarnHistoryEntry{ + .type = Data::EarnHistoryEntry::Type::Return, + .amount = d.vamount().v, + .date = base::unixtime::parse(d.vdate().v), + // .provider = qs(d.vprovider()), + }; + })); + } + const auto nextToken = token + tlTransactions.size(); + done(Data::EarnHistorySlice{ + .list = std::move(list), + .total = result.data().vcount().v, + .allLoaded = (result.data().vcount().v == nextToken), + .token = Data::EarnHistorySlice::OffsetToken(nextToken), + }); + }).fail([=] { + done({}); + _requestId = 0; + }).send(); +} + +Data::EarnStatistics EarnStatistics::data() const { + return _data; +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_statistics.h b/Telegram/SourceFiles/api/api_statistics.h index f5360adb2..f18cba71d 100644 --- a/Telegram/SourceFiles/api/api_statistics.h +++ b/Telegram/SourceFiles/api/api_statistics.h @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/timer.h" #include "data/data_boosts.h" +#include "data/data_channel_earn.h" #include "data/data_statistics.h" #include "mtproto/sender.h" @@ -107,6 +108,27 @@ private: }; +class EarnStatistics final : public StatisticsRequestSender { +public: + explicit EarnStatistics(not_null channel); + + [[nodiscard]] rpl::producer request(); + void requestHistory( + const Data::EarnHistorySlice::OffsetToken &token, + Fn done); + + [[nodiscard]] Data::EarnStatistics data() const; + + static constexpr auto kFirstSlice = int(5); + static constexpr auto kLimit = int(10); + +private: + Data::EarnStatistics _data; + + mtpRequestId _requestId = 0; + +}; + class Boosts final { public: explicit Boosts(not_null peer); diff --git a/Telegram/SourceFiles/api/api_text_entities.cpp b/Telegram/SourceFiles/api/api_text_entities.cpp index 2acbe5757..7fcb886bf 100644 --- a/Telegram/SourceFiles/api/api_text_entities.cpp +++ b/Telegram/SourceFiles/api/api_text_entities.cpp @@ -7,12 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "api/api_text_entities.h" -#include "main/main_session.h" +#include "data/data_document.h" +#include "data/data_session.h" +#include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_stickers_set.h" -#include "data/data_session.h" -#include "data/data_document.h" -#include "data/data_user.h" +#include "main/main_session.h" #include "base/qthelp_regex.h" #include "base/qthelp_url.h" @@ -64,67 +64,163 @@ using namespace TextUtilities; EntitiesInText EntitiesFromMTP( Main::Session *session, const QVector &entities) { + if (entities.isEmpty()) { + return {}; + } auto result = EntitiesInText(); - if (!entities.isEmpty()) { - result.reserve(entities.size()); - for (const auto &entity : entities) { - switch (entity.type()) { - case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back({ EntityType::Url, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back({ EntityType::CustomUrl, d.voffset().v, d.vlength().v, qs(d.vurl()) }); } break; - case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back({ EntityType::Email, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back({ EntityType::Hashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCashtag: { auto &d = entity.c_messageEntityCashtag(); result.push_back({ EntityType::Cashtag, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPhone: break; // Skipping phones. - case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back({ EntityType::Mention, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityMentionName: if (session) { - const auto &d = entity.c_messageEntityMentionName(); - const auto userId = UserId(d.vuser_id()); - const auto user = session->data().userLoaded(userId); - const auto data = MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = userId.bare, - .accessHash = user ? user->accessHash() : 0, - }); - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); - } break; - case mtpc_inputMessageEntityMentionName: if (session) { - const auto &d = entity.c_inputMessageEntityMentionName(); - const auto data = d.vuser_id().match([&]( - const MTPDinputUserSelf &) { - return MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = session->userId().bare, - .accessHash = session->user()->accessHash(), - }); - }, [&](const MTPDinputUser &data) { - return MentionNameDataFromFields({ - .selfId = session->userId().bare, - .userId = UserId(data.vuser_id()).bare, - .accessHash = data.vaccess_hash().v, - }); - }, [&](const auto &) { - return QString(); - }); - if (!data.isEmpty()) { - result.push_back({ EntityType::MentionName, d.voffset().v, d.vlength().v, data }); - } - } break; - case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back({ EntityType::BotCommand, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back({ EntityType::Bold, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back({ EntityType::Italic, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityUnderline: { auto &d = entity.c_messageEntityUnderline(); result.push_back({ EntityType::Underline, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityStrike: { auto &d = entity.c_messageEntityStrike(); result.push_back({ EntityType::StrikeOut, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back({ EntityType::Code, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back({ EntityType::Pre, d.voffset().v, d.vlength().v, qs(d.vlanguage()) }); } break; - case mtpc_messageEntityBlockquote: { auto &d = entity.c_messageEntityBlockquote(); result.push_back({ EntityType::Blockquote, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityBankCard: break; // Skipping cards. // #TODO entities - case mtpc_messageEntitySpoiler: { auto &d = entity.c_messageEntitySpoiler(); result.push_back({ EntityType::Spoiler, d.voffset().v, d.vlength().v }); } break; - case mtpc_messageEntityCustomEmoji: { - const auto &d = entity.c_messageEntityCustomEmoji(); - result.push_back({ EntityType::CustomEmoji, d.voffset().v, d.vlength().v, CustomEmojiEntityData(d) }); - } break; + result.reserve(entities.size()); + + for (const auto &entity : entities) { + entity.match([&](const MTPDmessageEntityUnknown &d) { + }, [&](const MTPDmessageEntityMention &d) { + result.push_back({ + EntityType::Mention, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityHashtag &d) { + result.push_back({ + EntityType::Hashtag, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBotCommand &d) { + result.push_back({ + EntityType::BotCommand, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityUrl &d) { + result.push_back({ + EntityType::Url, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityEmail &d) { + result.push_back({ + EntityType::Email, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBold &d) { + result.push_back({ + EntityType::Bold, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityItalic &d) { + result.push_back({ + EntityType::Italic, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityCode &d) { + result.push_back({ + EntityType::Code, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityPre &d) { + result.push_back({ + EntityType::Pre, + d.voffset().v, + d.vlength().v, + qs(d.vlanguage()), + }); + }, [&](const MTPDmessageEntityTextUrl &d) { + result.push_back({ + EntityType::CustomUrl, + d.voffset().v, + d.vlength().v, + qs(d.vurl()), + }); + }, [&](const MTPDmessageEntityMentionName &d) { + if (!session) { + return; } - } + const auto userId = UserId(d.vuser_id()); + const auto user = session->data().userLoaded(userId); + const auto data = MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = userId.bare, + .accessHash = user ? user->accessHash() : 0, + }); + result.push_back({ + EntityType::MentionName, + d.voffset().v, + d.vlength().v, + data, + }); + }, [&](const MTPDinputMessageEntityMentionName &d) { + if (!session) { + return; + } + const auto data = d.vuser_id().match([&]( + const MTPDinputUserSelf &) { + return MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = session->userId().bare, + .accessHash = session->user()->accessHash(), + }); + }, [&](const MTPDinputUser &data) { + return MentionNameDataFromFields({ + .selfId = session->userId().bare, + .userId = UserId(data.vuser_id()).bare, + .accessHash = data.vaccess_hash().v, + }); + }, [](const auto &) { + return QString(); + }); + if (!data.isEmpty()) { + result.push_back({ + EntityType::MentionName, + d.voffset().v, + d.vlength().v, + data, + }); + } + }, [&](const MTPDmessageEntityPhone &d) { + // Skipping phones. + }, [&](const MTPDmessageEntityCashtag &d) { + result.push_back({ + EntityType::Cashtag, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityUnderline &d) { + result.push_back({ + EntityType::Underline, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityStrike &d) { + result.push_back({ + EntityType::StrikeOut, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityBankCard &d) { + // Skipping cards. // #TODO entities + }, [&](const MTPDmessageEntitySpoiler &d) { + result.push_back({ + EntityType::Spoiler, + d.voffset().v, + d.vlength().v, + }); + }, [&](const MTPDmessageEntityCustomEmoji &d) { + result.push_back({ + EntityType::CustomEmoji, + d.voffset().v, + d.vlength().v, + CustomEmojiEntityData(d), + }); + }, [&](const MTPDmessageEntityBlockquote &d) { + result.push_back({ + EntityType::Blockquote, + d.voffset().v, + d.vlength().v, + }); + }); } return result; } @@ -136,7 +232,9 @@ MTPVector EntitiesToMTP( auto v = QVector(); v.reserve(entities.size()); for (const auto &entity : entities) { - if (entity.length() <= 0) continue; + if (entity.length() <= 0) { + continue; + } if (option == ConvertOption::SkipLocal && entity.type() != EntityType::Bold //&& entity.type() != EntityType::Semibold // Not in API. @@ -156,7 +254,9 @@ MTPVector EntitiesToMTP( auto offset = MTP_int(entity.offset()); auto length = MTP_int(entity.length()); switch (entity.type()) { - case EntityType::Url: v.push_back(MTP_messageEntityUrl(offset, length)); break; + case EntityType::Url: { + v.push_back(MTP_messageEntityUrl(offset, length)); + } break; case EntityType::CustomUrl: { auto url = entity.data(); auto inputUser = [&](const QString &data) -> MTPInputUser { @@ -194,26 +294,66 @@ MTPVector EntitiesToMTP( v.push_back(MTP_messageEntityTextUrl(offset, length, MTP_string(url))); } } break; - case EntityType::Email: v.push_back(MTP_messageEntityEmail(offset, length)); break; - case EntityType::Hashtag: v.push_back(MTP_messageEntityHashtag(offset, length)); break; - case EntityType::Cashtag: v.push_back(MTP_messageEntityCashtag(offset, length)); break; - case EntityType::Mention: v.push_back(MTP_messageEntityMention(offset, length)); break; + case EntityType::Email: { + v.push_back(MTP_messageEntityEmail(offset, length)); + } break; + case EntityType::Hashtag: { + v.push_back(MTP_messageEntityHashtag(offset, length)); + } break; + case EntityType::Cashtag: { + v.push_back(MTP_messageEntityCashtag(offset, length)); + } break; + case EntityType::Mention: { + v.push_back(MTP_messageEntityMention(offset, length)); + } break; case EntityType::MentionName: { - if (const auto valid = MentionNameEntity(session, offset, length, entity.data())) { + const auto valid = MentionNameEntity( + session, + offset, + length, + entity.data()); + if (valid) { v.push_back(*valid); } } break; - case EntityType::BotCommand: v.push_back(MTP_messageEntityBotCommand(offset, length)); break; - case EntityType::Bold: v.push_back(MTP_messageEntityBold(offset, length)); break; - case EntityType::Italic: v.push_back(MTP_messageEntityItalic(offset, length)); break; - case EntityType::Underline: v.push_back(MTP_messageEntityUnderline(offset, length)); break; - case EntityType::StrikeOut: v.push_back(MTP_messageEntityStrike(offset, length)); break; - case EntityType::Code: v.push_back(MTP_messageEntityCode(offset, length)); break; // #TODO entities - case EntityType::Pre: v.push_back(MTP_messageEntityPre(offset, length, MTP_string(entity.data()))); break; - case EntityType::Blockquote: v.push_back(MTP_messageEntityBlockquote(offset, length)); break; - case EntityType::Spoiler: v.push_back(MTP_messageEntitySpoiler(offset, length)); break; + case EntityType::BotCommand: { + v.push_back(MTP_messageEntityBotCommand(offset, length)); + } break; + case EntityType::Bold: { + v.push_back(MTP_messageEntityBold(offset, length)); + } break; + case EntityType::Italic: { + v.push_back(MTP_messageEntityItalic(offset, length)); + } break; + case EntityType::Underline: { + v.push_back(MTP_messageEntityUnderline(offset, length)); + } break; + case EntityType::StrikeOut: { + v.push_back(MTP_messageEntityStrike(offset, length)); + } break; + case EntityType::Code: { + // #TODO entities. + v.push_back(MTP_messageEntityCode(offset, length)); + } break; + case EntityType::Pre: { + v.push_back( + MTP_messageEntityPre( + offset, + length, + MTP_string(entity.data()))); + } break; + case EntityType::Blockquote: { + v.push_back(MTP_messageEntityBlockquote(offset, length)); + } break; + case EntityType::Spoiler: { + v.push_back(MTP_messageEntitySpoiler(offset, length)); + } break; case EntityType::CustomEmoji: { - if (const auto valid = CustomEmojiEntity(offset, length, entity.data())) { + const auto valid = CustomEmojiEntity( + offset, + length, + entity.data()); + if (valid) { v.push_back(*valid); } } break; diff --git a/Telegram/SourceFiles/api/api_transcribes.cpp b/Telegram/SourceFiles/api/api_transcribes.cpp index d7064abcd..dd983dc37 100644 --- a/Telegram/SourceFiles/api/api_transcribes.cpp +++ b/Telegram/SourceFiles/api/api_transcribes.cpp @@ -15,7 +15,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "history/history_item_helpers.h" -#include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" @@ -36,10 +35,10 @@ bool Transcribes::freeFor(not_null item) const { bool Transcribes::trialsSupport() { if (!_trialsSupport) { - const auto count = _session->account().appConfig().get( + const auto count = _session->appConfig().get( u"transcribe_audio_trial_weekly_number"_q, 0); - const auto until = _session->account().appConfig().get( + const auto until = _session->appConfig().get( u"transcribe_audio_trial_cooldown_until"_q, 0); _trialsSupport = (count > 0) || (until > 0); @@ -49,7 +48,7 @@ bool Transcribes::trialsSupport() { TimeId Transcribes::trialsRefreshAt() { if (_trialsRefreshAt < 0) { - _trialsRefreshAt = _session->account().appConfig().get( + _trialsRefreshAt = _session->appConfig().get( u"transcribe_audio_trial_cooldown_until"_q, 0); } @@ -58,7 +57,7 @@ TimeId Transcribes::trialsRefreshAt() { int Transcribes::trialsCount() { if (_trialsCount < 0) { - _trialsCount = _session->account().appConfig().get( + _trialsCount = _session->appConfig().get( u"transcribe_audio_trial_weekly_number"_q, -1); return std::max(_trialsCount, 0); @@ -67,7 +66,7 @@ int Transcribes::trialsCount() { } crl::time Transcribes::trialsMaxLengthMs() const { - return 1000 * _session->account().appConfig().get( + return 1000 * _session->appConfig().get( u"transcribe_audio_trial_duration_max"_q, 300); } diff --git a/Telegram/SourceFiles/api/api_updates.cpp b/Telegram/SourceFiles/api/api_updates.cpp index ab4b5f30b..314e6c862 100644 --- a/Telegram/SourceFiles/api/api_updates.cpp +++ b/Telegram/SourceFiles/api/api_updates.cpp @@ -21,6 +21,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_config.h" #include "mtproto/mtproto_dc_options.h" #include "data/business/data_shortcut_messages.h" +#include "data/components/scheduled_messages.h" +#include "data/components/top_peers.h" #include "data/notify/data_notify_settings.h" #include "data/stickers/data_stickers.h" #include "data/data_saved_messages.h" @@ -37,7 +39,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_histories.h" #include "data/data_folder.h" #include "data/data_forum.h" -#include "data/data_scheduled_messages.h" #include "data/data_send_action.h" #include "data/data_stories.h" #include "data/data_message_reactions.h" @@ -94,7 +95,7 @@ void ProcessScheduledMessageWithElapsedTime( // Note that when a message is scheduled until online // while the recipient is already online, the server sends // an ordinary new message with skipped "from_scheduled" flag. - session->data().scheduledMessages().checkEntitiesAndUpdate(data); + session->scheduledMessages().checkEntitiesAndUpdate(data); } } @@ -1120,6 +1121,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPPeer(), // saved_peer_id d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(), MTP_long(d.vvia_bot_id().value_or_empty()), + MTPlong(), // via_business_bot_id d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(), d.vdate(), d.vmessage(), @@ -1154,6 +1156,7 @@ void Updates::applyUpdatesNoPtsCheck(const MTPUpdates &updates) { MTPPeer(), // saved_peer_id d.vfwd_from() ? *d.vfwd_from() : MTPMessageFwdHeader(), MTP_long(d.vvia_bot_id().value_or_empty()), + MTPlong(), // via_business_bot_id d.vreply_to() ? *d.vreply_to() : MTPMessageReplyHeader(), d.vdate(), d.vmessage(), @@ -1462,7 +1465,9 @@ void Updates::applyUpdates( if (const auto id = owner.messageIdByRandomId(randomId)) { const auto local = owner.message(id); if (local && local->isScheduled()) { - owner.scheduledMessages().sendNowSimpleMessage(d, local); + session().scheduledMessages().sendNowSimpleMessage( + d, + local); } } const auto wasAlready = (lookupMessage() != nullptr); @@ -1559,7 +1564,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { auto &owner = session().data(); if (const auto local = owner.message(id)) { if (local->isScheduled()) { - session().data().scheduledMessages().apply(d, local); + session().scheduledMessages().apply(d, local); } else if (local->isBusinessShortcut()) { session().data().shortcutMessages().apply(d, local); } else { @@ -1573,6 +1578,11 @@ void Updates::feedUpdate(const MTPUpdate &update) { } else { if (existing) { existing->destroy(); + } else { + // Not the server-side date, but close enough. + session().topPeers().increment( + local->history()->peer, + local->date()); } local->setRealId(d.vid().v); } @@ -1769,12 +1779,12 @@ void Updates::feedUpdate(const MTPUpdate &update) { case mtpc_updateNewScheduledMessage: { const auto &d = update.c_updateNewScheduledMessage(); - session().data().scheduledMessages().apply(d); + session().scheduledMessages().apply(d); } break; case mtpc_updateDeleteScheduledMessages: { const auto &d = update.c_updateDeleteScheduledMessages(); - session().data().scheduledMessages().apply(d); + session().scheduledMessages().apply(d); } break; case mtpc_updateQuickReplies: { @@ -1941,7 +1951,7 @@ void Updates::feedUpdate(const MTPUpdate &update) { const auto &d = update.c_updatePeerSettings(); const auto peerId = peerFromMTP(d.vpeer()); if (const auto peer = session().data().peerLoaded(peerId)) { - peer->setSettings(d.vsettings()); + peer->setBarSettings(d.vsettings()); } } break; diff --git a/Telegram/SourceFiles/api/api_user_privacy.cpp b/Telegram/SourceFiles/api/api_user_privacy.cpp index edaae7c97..d0c17fa6b 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.cpp +++ b/Telegram/SourceFiles/api/api_user_privacy.cpp @@ -26,7 +26,9 @@ using TLInputRules = MTPVector; using TLRules = MTPVector; TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { - const auto collectInputUsers = [](const auto &peers) { + using Exceptions = UserPrivacy::Exceptions; + const auto collectInputUsers = [](const Exceptions &exceptions) { + const auto &peers = exceptions.peers; auto result = QVector(); result.reserve(peers.size()); for (const auto &peer : peers) { @@ -36,7 +38,8 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { } return result; }; - const auto collectInputChats = [](const auto &peers) { + const auto collectInputChats = [](const Exceptions &exceptions) { + const auto &peers = exceptions.peers; auto result = QVector(); result.reserve(peers.size()); for (const auto &peer : peers) { @@ -47,6 +50,7 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { return result; }; + using Option = UserPrivacy::Option; auto result = QVector(); result.reserve(kMaxRules); if (!rule.ignoreAlways) { @@ -62,6 +66,9 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { MTP_inputPrivacyValueAllowChatParticipants( MTP_vector(chats))); } + if (rule.always.premiums && (rule.option != Option::Everyone)) { + result.push_back(MTP_inputPrivacyValueAllowPremium()); + } } if (!rule.ignoreNever) { const auto users = collectInputUsers(rule.never); @@ -78,7 +85,6 @@ TLInputRules RulesToTL(const UserPrivacy::Rule &rule) { } } result.push_back([&] { - using Option = UserPrivacy::Option; switch (rule.option) { case Option::Everyone: return MTP_inputPrivacyValueAllowAll(); case Option::Contacts: return MTP_inputPrivacyValueAllowContacts(); @@ -101,12 +107,14 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) { auto result = UserPrivacy::Rule(); auto optionSet = false; const auto setOption = [&](Option option) { - if (optionSet) return; + if (optionSet) { + return; + } optionSet = true; result.option = option; }; - auto &always = result.always; - auto &never = result.never; + auto &always = result.always.peers; + auto &never = result.never.peers; const auto feed = [&](const MTPPrivacyRule &rule) { rule.match([&](const MTPDprivacyValueAllowAll &) { setOption(Option::Everyone); @@ -114,6 +122,8 @@ UserPrivacy::Rule TLToRules(const TLRules &rules, Data::Session &owner) { setOption(Option::Contacts); }, [&](const MTPDprivacyValueAllowCloseFriends &) { setOption(Option::CloseFriends); + }, [&](const MTPDprivacyValueAllowPremium &) { + result.always.premiums = true; }, [&](const MTPDprivacyValueAllowUsers &data) { const auto &users = data.vusers().v; always.reserve(always.size() + users.size()); @@ -188,6 +198,7 @@ MTPInputPrivacyKey KeyToTL(UserPrivacy::Key key) { case Key::ProfilePhoto: return MTP_inputPrivacyKeyProfilePhoto(); case Key::Voices: return MTP_inputPrivacyKeyVoiceMessages(); case Key::About: return MTP_inputPrivacyKeyAbout(); + case Key::Birthday: return MTP_inputPrivacyKeyBirthday(); } Unexpected("Key in Api::UserPrivacy::KetToTL."); } @@ -215,6 +226,8 @@ std::optional TLToKey(mtpTypeId type) { case mtpc_inputPrivacyKeyVoiceMessages: return Key::Voices; case mtpc_privacyKeyAbout: case mtpc_inputPrivacyKeyAbout: return Key::About; + case mtpc_privacyKeyBirthday: + case mtpc_inputPrivacyKeyBirthday: return Key::Birthday; } return std::nullopt; } @@ -294,8 +307,8 @@ void UserPrivacy::reload(Key key) { } void UserPrivacy::pushPrivacy(Key key, const TLRules &rules) { - const auto &saved = (_privacyValues[key] = - TLToRules(rules, _session->data())); + const auto &saved + = (_privacyValues[key] = TLToRules(rules, _session->data())); const auto i = _privacyChanges.find(key); if (i != end(_privacyChanges)) { i->second.fire_copy(saved); diff --git a/Telegram/SourceFiles/api/api_user_privacy.h b/Telegram/SourceFiles/api/api_user_privacy.h index be8e452d9..471f41f48 100644 --- a/Telegram/SourceFiles/api/api_user_privacy.h +++ b/Telegram/SourceFiles/api/api_user_privacy.h @@ -30,6 +30,7 @@ public: ProfilePhoto, Voices, About, + Birthday, }; enum class Option { Everyone, @@ -37,10 +38,14 @@ public: CloseFriends, Nobody, }; + struct Exceptions { + std::vector> peers; + bool premiums = false; + }; struct Rule { Option option = Option::Everyone; - std::vector> always; - std::vector> never; + Exceptions always; + Exceptions never; bool ignoreAlways = false; bool ignoreNever = false; }; diff --git a/Telegram/SourceFiles/api/api_who_reacted.cpp b/Telegram/SourceFiles/api/api_who_reacted.cpp index 27134dea9..f610f833d 100644 --- a/Telegram/SourceFiles/api/api_who_reacted.cpp +++ b/Telegram/SourceFiles/api/api_who_reacted.cpp @@ -24,7 +24,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_app_config.h" #include "main/main_session.h" -#include "main/main_account.h" #include "base/unixtime.h" #include "base/weak_ptr.h" #include "ui/controls/who_reacted_context_action.h" @@ -697,7 +696,7 @@ bool WhoReadExists(not_null item) { || user->readDatesPrivate()) { return false; } - const auto &appConfig = peer->session().account().appConfig(); + const auto &appConfig = peer->session().appConfig(); const auto expirePeriod = appConfig.get( "pm_read_date_expire_period", 7 * 86400); @@ -713,7 +712,7 @@ bool WhoReadExists(not_null item) { && (megagroup->flags() & ChannelDataFlag::ParticipantsHidden))) { return false; } - const auto &appConfig = peer->session().account().appConfig(); + const auto &appConfig = peer->session().appConfig(); const auto expirePeriod = appConfig.get( "chat_read_mark_expire_period", 7 * 86400); diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 34f0c88d7..25ae874ce 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_authorizations.h" #include "api/api_attached_stickers.h" #include "api/api_blocked_peers.h" +#include "api/api_chat_links.h" #include "api/api_chat_participants.h" #include "api/api_cloud_password.h" #include "api/api_hash.h" @@ -35,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_user_names.h" #include "api/api_websites.h" #include "data/business/data_shortcut_messages.h" +#include "data/components/scheduled_messages.h" #include "data/notify/data_notify_settings.h" #include "data/data_changes.h" #include "data/data_poll.h" @@ -44,13 +46,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_forum.h" #include "data/data_saved_sublist.h" #include "data/data_search_controller.h" -#include "data/data_scheduled_messages.h" #include "data/data_session.h" #include "data/data_channel.h" #include "data/data_chat.h" #include "data/data_user.h" #include "data/data_chat_filters.h" #include "data/data_histories.h" +#include "data/data_history_messages.h" #include "core/core_cloud_password.h" #include "core/application.h" #include "base/unixtime.h" @@ -165,6 +167,7 @@ ApiWrap::ApiWrap(not_null session) , _globalPrivacy(std::make_unique(this)) , _userPrivacy(std::make_unique(this)) , _inviteLinks(std::make_unique(this)) +, _chatLinks(std::make_unique(this)) , _views(std::make_unique(this)) , _confirmPhone(std::make_unique(this)) , _peerPhoto(std::make_unique(this)) @@ -542,7 +545,7 @@ void ApiWrap::sendMessageFail( } } } else if (error == u"SCHEDULE_STATUS_PRIVATE"_q) { - auto &scheduled = _session->data().scheduledMessages(); + auto &scheduled = _session->scheduledMessages(); Assert(peer->isUser()); if (const auto item = scheduled.lookupItem(peer->id, itemId.msg)) { scheduled.removeSending(item); @@ -796,7 +799,7 @@ QString ApiWrap::exportDirectStoryLink(not_null story) { const auto storyId = story->fullId(); const auto peer = story->peer(); const auto fallback = [&] { - const auto base = peer->userName(); + const auto base = peer->username(); const auto story = QString::number(storyId.story); const auto query = base + "/s/" + story; return session().createInternalLinkFull(query); @@ -1231,7 +1234,7 @@ void ApiWrap::requestPeerSettings(not_null peer) { result.match([&](const MTPDmessages_peerSettings &data) { _session->data().processUsers(data.vusers()); _session->data().processChats(data.vchats()); - peer->setSettings(data.vsettings()); + peer->setBarSettings(data.vsettings()); _requestedPeerSettings.erase(peer); }); }).fail([=] { @@ -1544,8 +1547,8 @@ void ApiWrap::saveStickerSets( writeRecent = true; } - const auto isAttached = - (removedSetId == Data::Stickers::CloudRecentAttachedSetId); + const auto isAttached + = (removedSetId == Data::Stickers::CloudRecentAttachedSetId); const auto flags = isAttached ? MTPmessages_ClearRecentStickers::Flag::f_attached : MTPmessages_ClearRecentStickers::Flags(0); @@ -1748,7 +1751,7 @@ void ApiWrap::joinChannel(not_null channel) { } return QString(); }(); - if (!text.isEmpty()) { + if (show && !text.isEmpty()) { show->showToast(text, kJoinErrorDuration); } } @@ -2447,8 +2450,8 @@ void ApiWrap::refreshFileReference( _session->data().peer(storyId.peer)->input, MTP_vector(1, MTP_int(storyId.story)))); } else if (item->isScheduled()) { - const auto &scheduled = _session->data().scheduledMessages(); - const auto realId = scheduled.lookupId(item); + const auto realId = _session->scheduledMessages().lookupId( + item); request(MTPmessages_GetScheduledMessages( item->history()->peer->input, MTP_vector(1, MTP_int(realId)))); @@ -2494,8 +2497,8 @@ void ApiWrap::refreshFileReference( }, [&](Data::FileOriginPeerPhoto data) { fail(); }, [&](Data::FileOriginStickerSet data) { - const auto isRecentAttached = - (data.setId == Data::Stickers::CloudRecentAttachedSetId); + const auto isRecentAttached + = (data.setId == Data::Stickers::CloudRecentAttachedSetId); if (data.setId == Data::Stickers::CloudRecentSetId || data.setId == Data::Stickers::RecentSetId || isRecentAttached) { @@ -3078,6 +3081,46 @@ void ApiWrap::resolveJumpToHistoryDate( } } +void ApiWrap::requestHistory( + not_null history, + MsgId messageId, + SliceType slice) { + const auto peer = history->peer; + const auto key = HistoryRequest{ + peer, + messageId, + slice, + }; + if (_historyRequests.contains(key)) { + return; + } + + const auto prepared = Api::PrepareHistoryRequest(peer, messageId, slice); + auto &histories = history->owner().histories(); + const auto requestType = Data::Histories::RequestType::History; + histories.sendRequest(history, requestType, [=](Fn finish) { + return request( + std::move(prepared) + ).done([=](const Api::HistoryRequestResult &result) { + _historyRequests.remove(key); + auto parsed = Api::ParseHistoryResult( + peer, + messageId, + slice, + result); + history->messages().addSlice( + std::move(parsed.messageIds), + parsed.noSkipRange, + parsed.fullCount); + finish(); + }).fail([=] { + _historyRequests.remove(key); + finish(); + }).send(); + }); + _historyRequests.emplace(key); +} + void ApiWrap::requestSharedMedia( not_null peer, MsgId topicRootId, @@ -4119,11 +4162,16 @@ void ApiWrap::cancelLocalItem(not_null item) { void ApiWrap::sendShortcutMessages( not_null peer, BusinessShortcutId id) { + auto ids = QVector(); + auto randomIds = QVector(); request(MTPmessages_SendQuickReplyMessages( peer->input, - MTP_int(id) + MTP_int(id), + MTP_vector(ids), + MTP_vector(randomIds) )).done([=](const MTPUpdates &result) { applyUpdates(result); + }).fail([=](const MTP::Error &error) { }).send(); } @@ -4365,13 +4413,14 @@ void ApiWrap::sendMessage( } void ApiWrap::sendBotStart( + std::shared_ptr show, not_null bot, PeerData *chat, const QString &startTokenForChat) { Expects(bot->isBot()); if (chat && chat->isChannel() && !chat->isMegagroup()) { - ShowAddParticipantsError("USER_BOT", chat, { 1, bot }); + ShowAddParticipantsError(show, "USER_BOT", chat, bot); return; } @@ -4403,7 +4452,7 @@ void ApiWrap::sendBotStart( }).fail([=](const MTP::Error &error) { if (chat) { const auto type = error.type(); - ShowAddParticipantsError(type, chat, { 1, bot }); + ShowAddParticipantsError(show, type, chat, bot); } }).send(); } @@ -4519,6 +4568,8 @@ void ApiWrap::uploadAlbumMedia( }; request(MTPmessages_UploadMedia( + MTP_flags(0), + MTPstring(), // business_connection_id item->history()->peer->input, media )).done([=](const MTPMessageMedia &result) { @@ -4934,6 +4985,10 @@ Api::InviteLinks &ApiWrap::inviteLinks() { return *_inviteLinks; } +Api::ChatLinks &ApiWrap::chatLinks() { + return *_chatLinks; +} + Api::ViewsManager &ApiWrap::views() { return *_views; } diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index df88db227..76d207fa7 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -53,6 +53,7 @@ class Key; namespace Ui { struct PreparedList; +class Show; } // namespace Ui namespace Api { @@ -69,6 +70,7 @@ class SensitiveContent; class GlobalPrivacy; class UserPrivacy; class InviteLinks; +class ChatLinks; class ViewsManager; class ConfirmPhone; class PeerPhoto; @@ -272,6 +274,10 @@ public: Fn, MsgId)> callback); using SliceType = Data::LoadDirection; + void requestHistory( + not_null history, + MsgId messageId, + SliceType slice); void requestSharedMedia( not_null peer, MsgId topicRootId, @@ -349,6 +355,7 @@ public: Fn doneCallback = nullptr, bool forwarding = false); void sendBotStart( + std::shared_ptr show, not_null bot, PeerData *chat = nullptr, const QString &startTokenForChat = QString()); @@ -391,6 +398,7 @@ public: [[nodiscard]] Api::GlobalPrivacy &globalPrivacy(); [[nodiscard]] Api::UserPrivacy &userPrivacy(); [[nodiscard]] Api::InviteLinks &inviteLinks(); + [[nodiscard]] Api::ChatLinks &chatLinks(); [[nodiscard]] Api::ViewsManager &views(); [[nodiscard]] Api::ConfirmPhone &confirmPhone(); [[nodiscard]] Api::PeerPhoto &peerPhoto(); @@ -514,7 +522,8 @@ private: not_null peer, bool justClear, bool revoke); - void applyAffectedMessages(const MTPmessages_AffectedMessages &result) const; + void applyAffectedMessages( + const MTPmessages_AffectedMessages &result) const; void deleteAllFromParticipantSend( not_null channel, @@ -648,6 +657,17 @@ private: }; base::flat_set _sharedMediaRequests; + struct HistoryRequest { + not_null peer; + MsgId aroundId = 0; + SliceType sliceType = {}; + + friend inline auto operator<=>( + const HistoryRequest&, + const HistoryRequest&) = default; + }; + base::flat_set _historyRequests; + std::unique_ptr _dialogsLoadState; TimeId _dialogsLoadTill = 0; rpl::variable _dialogsLoadMayBlockByDate = false; @@ -710,6 +730,7 @@ private: const std::unique_ptr _globalPrivacy; const std::unique_ptr _userPrivacy; const std::unique_ptr _inviteLinks; + const std::unique_ptr _chatLinks; const std::unique_ptr _views; const std::unique_ptr _confirmPhone; const std::unique_ptr _peerPhoto; diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index fd1fbfc4e..eabcdb22a 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -66,11 +66,12 @@ void ChatCreateDone( not_null navigation, QImage image, TimeId ttlPeriod, - const MTPUpdates &updates, + const MTPmessages_InvitedUsers &result, Fn)> done) { - navigation->session().api().applyUpdates(updates); + const auto &data = result.data(); + navigation->session().api().applyUpdates(data.vupdates()); - const auto success = base::make_optional(&updates) + const auto success = base::make_optional(&data.vupdates()) | [](auto updates) -> std::optional*> { switch (updates->type()) { case mtpc_updates: @@ -109,7 +110,7 @@ void ChatCreateDone( ChatInviteForbidden( show, chat, - CollectForbiddenUsers(&chat->session(), updates)); + CollectForbiddenUsers(&chat->session(), result)); } }; if (!success) { @@ -168,39 +169,61 @@ TextWithEntities PeerFloodErrorText( } void ShowAddParticipantsError( + std::shared_ptr show, const QString &error, not_null chat, - const std::vector> &users, - std::shared_ptr show) { + not_null user) { + ShowAddParticipantsError( + std::move(show), + error, + chat, + { .users = { 1, user } }); +} + +void ShowAddParticipantsError( + std::shared_ptr show, + const QString &error, + not_null chat, + const ForbiddenInvites &forbidden) { if (error == u"USER_BOT"_q) { const auto channel = chat->asChannel(); - if ((users.size() == 1) - && users.front()->isBot() + if ((forbidden.users.size() == 1) + && forbidden.users.front()->isBot() && channel && !channel->isMegagroup() && channel->canAddAdmins()) { - const auto makeAdmin = [=] { - const auto user = users.front(); + const auto makeAdmin = [=](Fn close) { + const auto user = forbidden.users.front(); const auto weak = std::make_shared>(); - const auto close = [=](auto&&...) { - if (*weak) { - (*weak)->closeBox(); + const auto done = [=](auto&&...) { + if (const auto strong = weak->data()) { + strong->uiShow()->showToast( + tr::lng_box_done(tr::now)); + strong->closeBox(); + } + }; + const auto fail = [=] { + if (const auto strong = weak->data()) { + strong->closeBox(); } }; const auto saveCallback = SaveAdminCallback( + show, channel, user, - close, - close); + done, + fail); auto box = Box( channel, user, ChatAdminRightsInfo(), QString()); box->setSaveCallback(saveCallback); - *weak = Ui::show(std::move(box)); + *weak = box.data(); + show->showBox(std::move(box)); + close(); }; - Ui::show( + show->showBox( Ui::MakeConfirmBox({ .text = tr::lng_cant_invite_offer_admin(), .confirmed = makeAdmin, @@ -210,7 +233,7 @@ void ShowAddParticipantsError( return; } } - const auto hasBot = ranges::any_of(users, &UserData::isBot); + const auto hasBot = ranges::any_of(forbidden.users, &UserData::isBot); if (error == u"PEER_FLOOD"_q) { const auto type = (chat->isChat() || chat->isMegagroup()) ? PeerFloodType::InviteGroup @@ -218,8 +241,8 @@ void ShowAddParticipantsError( const auto text = PeerFloodErrorText(&chat->session(), type); Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); return; - } else if (error == u"USER_PRIVACY_RESTRICTED"_q && show) { - ChatInviteForbidden(show, chat, users); + } else if (error == u"USER_PRIVACY_RESTRICTED"_q) { + ChatInviteForbidden(show, chat, forbidden); return; } const auto text = [&] { @@ -230,8 +253,6 @@ void ShowAddParticipantsError( } else if (error == u"USER_KICKED"_q) { // Trying to return a user who was kicked by admin. return tr::lng_cant_invite_banned(tr::now); - } else if (error == u"USER_PRIVACY_RESTRICTED"_q) { - return tr::lng_cant_invite_privacy(tr::now); } else if (error == u"USER_NOT_MUTUAL_CONTACT"_q) { // Trying to return user who does not have me in contacts. return tr::lng_failed_add_not_mutual(tr::now); @@ -246,7 +267,7 @@ void ShowAddParticipantsError( } return tr::lng_failed_add_participant(tr::now); }(); - Ui::show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); + show->show(Ui::MakeInformBox(text), Ui::LayerOption::KeepOther); } AddContactBox::AddContactBox( @@ -588,8 +609,8 @@ void GroupInfoBox::prepare() { _navigation->session().api().selfDestruct().reload(); const auto top = addTopButton(st::infoTopBarMenu); - const auto menu = - top->lifetime().make_state>(); + const auto menu + = top->lifetime().make_state>(); top->setClickedCallback([=] { *menu = base::make_unique_q( top, @@ -704,7 +725,7 @@ void GroupInfoBox::createGroup( MTP_vector(inputs), MTP_string(title), MTP_int(ttlPeriod()) - )).done([=](const MTPUpdates &result) { + )).done([=](const MTPmessages_InvitedUsers &result) { auto image = _photo->takeResultImage(); const auto period = ttlPeriod(); const auto navigation = _navigation; @@ -1289,8 +1310,8 @@ void SetupChannelBox::handleChange() { && (ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '_') { - const auto badSymbols = - tr::lng_create_channel_link_bad_symbols(tr::now); + const auto badSymbols + = tr::lng_create_channel_link_bad_symbols(tr::now); if (_errorText != badSymbols) { _errorText = badSymbols; update(); @@ -1300,8 +1321,8 @@ void SetupChannelBox::handleChange() { } } if (name.size() < Ui::EditPeer::kMinUsernameLength) { - const auto tooShort = - tr::lng_create_channel_link_too_short(tr::now); + const auto tooShort + = tr::lng_create_channel_link_too_short(tr::now); if (_errorText != tooShort) { _errorText = tooShort; update(); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.h b/Telegram/SourceFiles/boxes/add_contact_box.h index 6d19a2d7f..9bc7de652 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.h +++ b/Telegram/SourceFiles/boxes/add_contact_box.h @@ -34,6 +34,7 @@ template class Radioenum; class LinkButton; class UserpicButton; +class Show; } // namespace Ui enum class PeerFloodType { @@ -42,14 +43,21 @@ enum class PeerFloodType { InviteChannel, }; +struct ForbiddenInvites; + [[nodiscard]] TextWithEntities PeerFloodErrorText( not_null session, PeerFloodType type); void ShowAddParticipantsError( + std::shared_ptr show, const QString &error, not_null chat, - const std::vector> &users, - std::shared_ptr show = nullptr); + const ForbiddenInvites &forbidden); +void ShowAddParticipantsError( + std::shared_ptr show, + const QString &error, + not_null chat, + not_null user); class AddContactBox : public Ui::BoxContent { public: diff --git a/Telegram/SourceFiles/boxes/background_box.cpp b/Telegram/SourceFiles/boxes/background_box.cpp index 877cf556e..59729b7fe 100644 --- a/Telegram/SourceFiles/boxes/background_box.cpp +++ b/Telegram/SourceFiles/boxes/background_box.cpp @@ -40,11 +40,11 @@ namespace { constexpr auto kBackgroundsInRow = 3; QImage TakeMiddleSample(QImage original, QSize size) { - size *= cIntRetinaFactor(); + size *= style::DevicePixelRatio(); const auto from = original.size(); if (from.isEmpty()) { auto result = original.scaled(size); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(style::DevicePixelRatio()); return result; } @@ -58,7 +58,7 @@ QImage TakeMiddleSample(QImage original, QSize size) { take.width(), take.height() ).scaled(size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); - result.setDevicePixelRatio(cRetinaFactor()); + result.setDevicePixelRatio(style::DevicePixelRatio()); return result; } @@ -657,10 +657,10 @@ void BackgroundBox::Inner::validatePaperThumbnail( } else if (!paper.data.backgroundColors().empty()) { paper.thumbnail = Ui::PixmapFromImage( Ui::GenerateBackgroundImage( - st::backgroundSize * cIntRetinaFactor(), + st::backgroundSize * style::DevicePixelRatio(), paper.data.backgroundColors(), paper.data.gradientRotation())); - paper.thumbnail.setDevicePixelRatio(cRetinaFactor()); + paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio()); return; } else { return; @@ -680,7 +680,7 @@ void BackgroundBox::Inner::validatePaperThumbnail( paper.thumbnail = Ui::PixmapFromImage(TakeMiddleSample( original, st::backgroundSize)); - paper.thumbnail.setDevicePixelRatio(cRetinaFactor()); + paper.thumbnail.setDevicePixelRatio(style::DevicePixelRatio()); } bool BackgroundBox::Inner::forChannel() const { diff --git a/Telegram/SourceFiles/boxes/background_preview_box.cpp b/Telegram/SourceFiles/boxes/background_preview_box.cpp index d445ce1f5..828b2d095 100644 --- a/Telegram/SourceFiles/boxes/background_preview_box.cpp +++ b/Telegram/SourceFiles/boxes/background_preview_box.cpp @@ -467,14 +467,14 @@ void BackgroundPreviewBox::generateBackground() { return; } const auto size = QSize(st::boxWideWidth, st::boxWideWidth) - * cIntRetinaFactor(); + * style::DevicePixelRatio(); _generated = Ui::PixmapFromImage((_paper.patternOpacity() >= 0.) ? Ui::GenerateBackgroundImage( size, _paper.backgroundColors(), _paper.gradientRotation()) : BlackImage(size)); - _generated.setDevicePixelRatio(cRetinaFactor()); + _generated.setDevicePixelRatio(style::DevicePixelRatio()); } not_null BackgroundPreviewBox::delegate() { @@ -593,11 +593,11 @@ void BackgroundPreviewBox::uploadForPeer(bool both) { const auto ready = Window::Theme::PrepareWallPaper( session->mainDcId(), _paper.localThumbnail()->original()); - const auto documentId = ready.id; + const auto documentId = ready->id; _uploadId = FullMsgId( session->userPeerId(), session->data().nextLocalMessageId()); - session->uploader().uploadMedia(_uploadId, ready); + session->uploader().upload(_uploadId, ready); if (_uploadLifetime) { return; } @@ -889,7 +889,7 @@ void BackgroundPreviewBox::paintEvent(QPaintEvent *e) { void BackgroundPreviewBox::paintImage(Painter &p) { Expects(!_scaled.isNull()); - const auto factor = cIntRetinaFactor(); + const auto factor = style::DevicePixelRatio(); const auto size = st::boxWideWidth; const auto from = QRect( 0, @@ -940,7 +940,7 @@ int BackgroundPreviewBox::textsTop() const { - st::historyPaddingBottom - (_service ? _service->height() : 0) - _text1->height() - - (forChannel() ? _text2->height() : 0); + - (forChannel() ? 0 : _text2->height()); } QRect BackgroundPreviewBox::radialRect() const { diff --git a/Telegram/SourceFiles/boxes/boxes.style b/Telegram/SourceFiles/boxes/boxes.style index 193981115..5c042c27e 100644 --- a/Telegram/SourceFiles/boxes/boxes.style +++ b/Telegram/SourceFiles/boxes/boxes.style @@ -546,7 +546,7 @@ rightsToggle: Toggle(defaultToggle) { vsize: 5px; vshift: 1px; stroke: 2px; - duration: 120; + duration: universalDuration; } rightsButton: SettingsButton(defaultSettingsButton) { @@ -691,6 +691,10 @@ createPollOptionField: InputField(createPollField) { placeholderMargins: margins(2px, 0px, 2px, 0px); heightMax: 68px; } +createPollOptionFieldPremium: InputField(createPollOptionField) { + textMargins: margins(22px, 11px, 68px, 11px); +} +createPollOptionFieldPremiumEmojiPosition: point(15px, -1px); createPollSolutionField: InputField(createPollField) { textMargins: margins(0px, 4px, 0px, 4px); border: 1px; @@ -704,7 +708,7 @@ createPollOptionRemove: CrossButton { cross: CrossAnimation { size: 22px; skip: 6px; - stroke: 1.; + stroke: 1.5; minScale: 0.3; } crossFg: boxTitleCloseFg; @@ -718,6 +722,7 @@ createPollOptionRemove: CrossButton { } } createPollOptionRemovePosition: point(11px, 9px); +createPollOptionEmojiPositionSkip: 4px; createPollWarning: FlatLabel(defaultFlatLabel) { textFg: windowSubTextFg; palette: TextPalette(defaultTextPalette) { @@ -1026,6 +1031,80 @@ shortInfoBox: ShortInfoBox { labeledOneLine: infoLabeledOneLine; } +birthdayLabeled: FlatLabel(infoLabeled) { + margin: margins(0px, 0px, 0px, 0px); +} +birthdayLabel: FlatLabel(infoLabel) { + margin: margins(0px, 0px, 0px, 0px); +} +birthdayTodayIcon: icon {{ "menu/gift_premium", windowActiveTextFg }}; + +inviteForbiddenUserpicsPadding: margins(10px, 10px, 10px, 0px); +inviteForbiddenInfo: FlatLabel(defaultFlatLabel) { + minWidth: 240px; + align: align(top); +} +inviteForbiddenInfoPadding: margins(32px, 10px, 32px, 4px); +inviteForbiddenSubscribePadding: margins(16px, 12px, 16px, 16px); +inviteForbiddenOrLabelPadding: margins(32px, 0px, 32px, 0px); +inviteForbiddenTitle: FlatLabel(boxTitle) { + minWidth: 120px; + align: align(top); +} +inviteForbiddenTitlePadding: margins(32px, 4px, 32px, 0px); +inviteForbiddenLockBg: dialogsUnreadBgMuted; +inviteForbiddenLockIcon: icon {{ "emoji/premium_lock", dialogsUnreadFg }}; + +collectibleIconDiameter: 72px; +collectibleIcon: 64px; +collectibleIconPadding: margins(24px, 32px, 24px, 12px); +collectibleHeader: FlatLabel(boxTitle) { + minWidth: 120px; + maxHeight: 0px; + align: align(top); +} +collectibleHeaderPadding: margins(24px, 16px, 24px, 12px); +collectibleOwnerPadding: margins(24px, 4px, 24px, 8px); +collectibleInfo: inviteForbiddenInfo; +collectibleInfoPadding: margins(24px, 12px, 24px, 12px); +collectibleInfoTonMargins: margins(0px, 3px, 0px, 0px); +collectibleMore: RoundButton(defaultActiveButton) { + height: 36px; + textTop: 9px; + radius: 6px; +} +collectibleMorePadding: margins(24px, 12px, 24px, 0px); +collectibleCopy: RoundButton(defaultLightButton) { + height: 36px; + textTop: 9px; + radius: 6px; +} +collectibleBox: Box(defaultBox) { + buttonPadding: margins(24px, 12px, 24px, 12px); + buttonHeight: 36px; + button: collectibleCopy; +} + +moderateBoxUserpic: UserpicButton(defaultUserpicButton) { + size: size(34px, 42px); + photoSize: 34px; + photoPosition: point(0px, 4px); +} +moderateBoxExpand: icon {{ "chat/reply_type_group", boxTextFg }}; +moderateBoxExpandHeight: 20px; +moderateBoxExpandRight: 10px; +moderateBoxExpandInnerSkip: 2px; +moderateBoxExpandFont: font(11px); +moderateBoxExpandToggleSize: 4px; +moderateBoxExpandToggleFourStrokes: 3px; +moderateBoxExpandIcon: icon{{ "info/edit/expand_arrow_small-flip_vertical", windowActiveTextFg }}; +moderateBoxExpandIconDown: icon{{ "info/edit/expand_arrow_small", windowActiveTextFg }}; +moderateBoxDividerLabel: FlatLabel(boxDividerLabel) { + palette: TextPalette(defaultTextPalette) { + selectLinkFg: windowActiveTextFg; + } +} + fontsBoxTextStyle: TextStyle(defaultTextStyle) { font: font(13px); } diff --git a/Telegram/SourceFiles/boxes/connection_box.cpp b/Telegram/SourceFiles/boxes/connection_box.cpp index 3c88a19ef..a90dd702e 100644 --- a/Telegram/SourceFiles/boxes/connection_box.cpp +++ b/Telegram/SourceFiles/boxes/connection_box.cpp @@ -8,31 +8,35 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/connection_box.h" #include "kotato/kotato_lang.h" -#include "ui/boxes/confirm_box.h" -#include "lang/lang_keys.h" -#include "storage/localstorage.h" -#include "base/qthelp_url.h" #include "base/call_delayed.h" +#include "base/qthelp_regex.h" +#include "base/qthelp_url.h" #include "core/application.h" +#include "core/click_handler_types.h" #include "core/core_settings.h" +#include "core/local_url_handlers.h" +#include "lang/lang_keys.h" #include "main/main_account.h" #include "mtproto/facade.h" -#include "ui/widgets/checkbox.h" +#include "storage/localstorage.h" +#include "ui/basic_click_handlers.h" +#include "ui/boxes/confirm_box.h" +#include "ui/effects/animations.h" +#include "ui/effects/radial_animation.h" +#include "ui/painter.h" +#include "ui/text/text_options.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" #include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/dropdown_menu.h" #include "ui/widgets/fields/input_field.h" #include "ui/widgets/fields/number_input.h" #include "ui/widgets/fields/password_input.h" #include "ui/widgets/labels.h" -#include "ui/widgets/dropdown_menu.h" +#include "ui/widgets/popup_menu.h" #include "ui/wrap/slide_wrap.h" #include "ui/wrap/vertical_layout.h" -#include "ui/toast/toast.h" -#include "ui/effects/animations.h" -#include "ui/effects/radial_animation.h" -#include "ui/text/text_options.h" -#include "ui/text/text_utilities.h" -#include "ui/basic_click_handlers.h" -#include "ui/painter.h" #include "boxes/abstract_box.h" // Ui::show(). #include "styles/style_layers.h" #include "styles/style_boxes.h" @@ -49,6 +53,22 @@ constexpr auto kSaveSettingsDelayedTimeout = crl::time(1000); using ProxyData = MTP::ProxyData; +[[nodiscard]] ProxyData ProxyDataFromFields( + ProxyData::Type type, + const QMap &fields) { + auto proxy = ProxyData(); + proxy.type = type; + proxy.host = fields.value(u"server"_q); + proxy.port = fields.value(u"port"_q).toUInt(); + if (type == ProxyData::Type::Socks5) { + proxy.user = fields.value(u"user"_q); + proxy.password = fields.value(u"pass"_q); + } else if (type == ProxyData::Type::Mtproto) { + proxy.password = fields.value(u"secret"_q); + } + return proxy; +}; + class HostInput : public Ui::MaskedInputField { public: HostInput( @@ -204,6 +224,7 @@ protected: private: void setupContent(); + void setupTopButton(); void createNoRowsLabel(); void addNewProxy(); void applyView(View &&view); @@ -601,9 +622,80 @@ void ProxiesBox::prepare() { addButton(tr::lng_proxy_add(), [=] { addNewProxy(); }); addButton(tr::lng_close(), [=] { closeBox(); }); + setupTopButton(); setupContent(); } +void ProxiesBox::setupTopButton() { + const auto top = addTopButton(st::infoTopBarMenu); + const auto menu + = top->lifetime().make_state>(); + const auto callback = [=] { + const auto maybeUrl = QGuiApplication::clipboard()->text(); + const auto local = Core::TryConvertUrlToLocal(maybeUrl); + + const auto proxyString = u"proxy"_q; + const auto socksString = u"socks"_q; + const auto protocol = u"tg://"_q; + const auto command = base::StringViewMid( + local, + protocol.size(), + 8192); + + if (local.startsWith(protocol + proxyString) + || local.startsWith(protocol + socksString)) { + + using namespace qthelp; + const auto options = RegExOption::CaseInsensitive; + for (const auto &[expression, _] : Core::LocalUrlHandlers()) { + const auto midExpression = base::StringViewMid( + expression, + 1); + const auto isSocks = midExpression.startsWith( + socksString); + if (!midExpression.startsWith(proxyString) + && !isSocks) { + continue; + } + const auto match = regex_match( + expression, + command, + options); + if (!match) { + continue; + } + const auto type = isSocks + ? ProxyData::Type::Socks5 + : ProxyData::Type::Mtproto; + const auto fields = url_parse_params( + match->captured(1), + qthelp::UrlParamNameTransform::ToLower); + const auto proxy = ProxyDataFromFields(type, fields); + const auto contains = _controller->contains(proxy); + const auto toast = (contains + ? tr::lng_proxy_add_from_clipboard_existing_toast + : tr::lng_proxy_add_from_clipboard_good_toast)(tr::now); + uiShow()->showToast(toast); + if (!contains) { + _controller->addNewItem(proxy); + } + break; + } + } else { + uiShow()->showToast( + tr::lng_proxy_add_from_clipboard_failed_toast(tr::now)); + } + }; + top->setClickedCallback([=] { + *menu = base::make_unique_q(top, st::defaultPopupMenu); + (*menu)->addAction( + tr::lng_proxy_add_from_clipboard(tr::now), + callback); + (*menu)->popup(QCursor::pos()); + return true; + }); +} + void ProxiesBox::setupContent() { const auto inner = setInnerWidget(object_ptr(this)); @@ -1097,24 +1189,13 @@ ProxiesBoxController::ProxiesBoxController(not_null account) void ProxiesBoxController::ShowApplyConfirmation( Type type, const QMap &fields) { - const auto server = fields.value(u"server"_q); - const auto port = fields.value(u"port"_q).toUInt(); - auto proxy = ProxyData(); - proxy.type = type; - proxy.host = server; - proxy.port = port; - if (type == Type::Socks5) { - proxy.user = fields.value(u"user"_q); - proxy.password = fields.value(u"pass"_q); - } else if (type == Type::Mtproto) { - proxy.password = fields.value(u"secret"_q); - } + const auto proxy = ProxyDataFromFields(type, fields); if (proxy) { static const auto UrlStartRegExp = QRegularExpression( "^https://", QRegularExpression::CaseInsensitiveOption); static const auto UrlEndRegExp = QRegularExpression("/$"); - const auto displayed = "https://" + server + "/"; + const auto displayed = "https://" + proxy.host + "/"; const auto parsed = QUrl::fromUserInput(displayed); const auto displayUrl = !UrlClickHandler::IsSuspicious(displayed) ? displayed @@ -1132,7 +1213,7 @@ void ProxiesBoxController::ShowApplyConfirmation( lt_server, displayServer, lt_port, - QString::number(port)) + QString::number(proxy.port)) + (proxy.type == Type::Mtproto ? "\n\n" + tr::lng_proxy_sponsor_warning(tr::now) : QString()); @@ -1449,6 +1530,14 @@ object_ptr ProxiesBoxController::addNewItemBox() { }); } +bool ProxiesBoxController::contains(const ProxyData &proxy) const { + const auto j = ranges::find( + _list, + proxy, + [](const Item &item) { return item.data; }); + return (j != end(_list)); +} + void ProxiesBoxController::addNewItem(const ProxyData &proxy) { auto &proxies = _settings.list(); proxies.push_back(proxy); diff --git a/Telegram/SourceFiles/boxes/connection_box.h b/Telegram/SourceFiles/boxes/connection_box.h index 8c78ebf34..e3bbfd006 100644 --- a/Telegram/SourceFiles/boxes/connection_box.h +++ b/Telegram/SourceFiles/boxes/connection_box.h @@ -77,6 +77,9 @@ public: void setTryIPv6(bool enabled); rpl::producer proxySettingsValue() const; + [[nodiscard]] bool contains(const ProxyData &proxy) const; + void addNewItem(const ProxyData &proxy); + rpl::producer views() const; ~ProxiesBoxController(); @@ -109,7 +112,6 @@ private: void replaceItemValue( std::vector::iterator which, const ProxyData &proxy); - void addNewItem(const ProxyData &proxy); const not_null _account; Core::SettingsProxy &_settings; diff --git a/Telegram/SourceFiles/boxes/create_poll_box.cpp b/Telegram/SourceFiles/boxes/create_poll_box.cpp index 2ef850d04..c2901ae5f 100644 --- a/Telegram/SourceFiles/boxes/create_poll_box.cpp +++ b/Telegram/SourceFiles/boxes/create_poll_box.cpp @@ -7,33 +7,41 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "boxes/create_poll_box.h" -#include "lang/lang_keys.h" -#include "data/data_poll.h" -#include "ui/toast/toast.h" -#include "ui/wrap/vertical_layout.h" -#include "ui/wrap/slide_wrap.h" -#include "ui/wrap/fade_wrap.h" -#include "ui/widgets/fields/input_field.h" -#include "ui/widgets/shadow.h" -#include "ui/widgets/labels.h" -#include "ui/widgets/buttons.h" -#include "ui/widgets/checkbox.h" -#include "ui/text/text_utilities.h" -#include "ui/vertical_list.h" -#include "main/main_session.h" -#include "core/application.h" -#include "core/core_settings.h" +#include "base/call_delayed.h" +#include "base/event_filter.h" +#include "base/random.h" +#include "base/unique_qptr.h" #include "chat_helpers/emoji_suggestions_widget.h" #include "chat_helpers/message_field.h" -#include "menu/menu_send.h" +#include "chat_helpers/tabbed_panel.h" +#include "chat_helpers/tabbed_selector.h" +#include "core/application.h" +#include "core/core_settings.h" +#include "data/data_poll.h" +#include "data/data_user.h" +#include "data/stickers/data_custom_emoji.h" #include "history/view/history_view_schedule_box.h" -#include "base/unique_qptr.h" -#include "base/event_filter.h" -#include "base/call_delayed.h" -#include "base/random.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" +#include "menu/menu_send.h" +#include "ui/controls/emoji_button.h" +#include "ui/rect.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" +#include "ui/vertical_list.h" +#include "ui/widgets/buttons.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/fields/input_field.h" +#include "ui/widgets/labels.h" +#include "ui/widgets/shadow.h" +#include "ui/wrap/fade_wrap.h" +#include "ui/wrap/slide_wrap.h" +#include "ui/wrap/vertical_layout.h" #include "window/window_session_controller.h" -#include "styles/style_layers.h" #include "styles/style_boxes.h" +#include "styles/style_chat_helpers.h" // defaultComposeFiles. +#include "styles/style_layers.h" +#include "styles/style_settings.h" namespace { @@ -46,12 +54,107 @@ constexpr auto kSolutionLimit = 200; constexpr auto kWarnSolutionLimit = 60; constexpr auto kErrorLimit = 99; +[[nodiscard]] not_null AddEmojiToggleToField( + not_null field, + not_null box, + not_null controller, + not_null emojiPanel, + QPoint shift) { + const auto emojiToggle = Ui::CreateChild( + field->parentWidget(), + st::defaultComposeFiles.emoji); + const auto fade = Ui::CreateChild( + emojiToggle, + emojiToggle, + 0.5); + { + const auto fadeTarget = Ui::CreateChild(emojiToggle); + fadeTarget->resize(emojiToggle->size()); + fadeTarget->paintRequest( + ) | rpl::start_with_next([=](const QRect &rect) { + auto p = QPainter(fadeTarget); + if (fade->animating()) { + p.fillRect(fadeTarget->rect(), st::boxBg); + } + fade->paint(p); + }, fadeTarget->lifetime()); + rpl::single(false) | rpl::then( + field->focusedChanges() + ) | rpl::start_with_next([=](bool shown) { + if (shown) { + fade->fadeIn(st::universalDuration); + } else { + fade->fadeOut(st::universalDuration); + } + }, emojiToggle->lifetime()); + fade->fadeOut(1); + fade->finish(); + } + + + const auto outer = box->getDelegate()->outerContainer(); + const auto allow = [](not_null) { return true; }; + InitMessageFieldHandlers( + controller, + field, + Window::GifPauseReason::Layer, + allow); + Ui::Emoji::SuggestionsController::Init( + outer, + field, + &controller->session(), + Ui::Emoji::SuggestionsController::Options{ + .suggestCustomEmoji = true, + .allowCustomWithoutPremium = allow, + }); + const auto updateEmojiPanelGeometry = [=] { + const auto parent = emojiPanel->parentWidget(); + const auto global = emojiToggle->mapToGlobal({ 0, 0 }); + const auto local = parent->mapFromGlobal(global); + const auto right = local.x() + emojiToggle->width() * 3; + const auto isDropDown = local.y() < parent->height() / 2; + emojiPanel->setDropDown(isDropDown); + if (isDropDown) { + emojiPanel->moveTopRight( + local.y() + emojiToggle->height(), + right); + } else { + emojiPanel->moveBottomRight(local.y(), right); + } + }; + rpl::combine( + box->sizeValue(), + field->geometryValue() + ) | rpl::start_with_next([=](QSize outer, QRect inner) { + emojiToggle->moveToLeft( + rect::right(inner) + shift.x(), + inner.y() + shift.y()); + emojiToggle->update(); + }, emojiToggle->lifetime()); + + emojiToggle->installEventFilter(emojiPanel); + emojiToggle->addClickHandler([=] { + updateEmojiPanelGeometry(); + emojiPanel->toggleAnimated(); + }); + const auto filterCallback = [=](not_null event) { + if (event->type() == QEvent::Enter) { + updateEmojiPanelGeometry(); + } + return base::EventFilterResult::Continue; + }; + base::install_event_filter(emojiToggle, filterCallback); + + return emojiToggle; +} + class Options { public: Options( - not_null outer, + not_null box, not_null container, - not_null session, + not_null controller, + ChatHelpers::TabbedPanel *emojiPanel, bool chooseCorrectEnabled); [[nodiscard]] bool hasOptions() const; @@ -140,9 +243,10 @@ private: [[nodiscard]] auto createChooseCorrectGroup() -> std::shared_ptr; - not_null _outer; + not_null _box; not_null _container; - const not_null _session; + const not_null _controller; + ChatHelpers::TabbedPanel * const _emojiPanel; std::shared_ptr _chooseCorrectGroup; int _position = 0; std::vector> _list; @@ -154,6 +258,7 @@ private: rpl::event_stream> _scrollToWidget; rpl::event_stream<> _backspaceInFront; rpl::event_stream<> _tabbed; + rpl::lifetime _emojiPanelLifetime; }; @@ -191,8 +296,9 @@ not_null CreateWarningLabel( if (value >= 0) { result->setText(QString::number(value)); } else { + constexpr auto kMinus = QChar(0x2212); result->setMarkedText(Ui::Text::Colorized( - QString::number(value))); + kMinus + QString::number(std::abs(value)))); } result->setVisible(shown); })); @@ -221,7 +327,9 @@ Options::Option::Option( , _field( Ui::CreateChild( _content.get(), - st::createPollOptionField, + session->user()->isPremium() + ? st::createPollOptionFieldPremium + : st::createPollOptionField, Ui::InputField::Mode::NoNewlines, tr::lng_polls_create_option_add())) { InitField(outer, _field, session); @@ -297,7 +405,7 @@ void Options::Option::createRemove() { const auto remove = Ui::CreateChild( field.get(), st::createPollOptionRemove); - remove->hide(anim::type::instant); + remove->show(anim::type::instant); const auto toggle = lifetime.make_state>(false); _removeAlways = lifetime.make_state>(false); @@ -307,6 +415,7 @@ void Options::Option::createRemove() { // Don't capture 'this'! Because Option is a value type. *toggle = !field->getLastText().isEmpty(); }, field->lifetime()); +#if 0 rpl::combine( toggle->value(), _removeAlways->value(), @@ -314,6 +423,7 @@ void Options::Option::createRemove() { ) | rpl::start_with_next([=](bool shown) { remove->toggle(shown, anim::type::normal); }, remove->lifetime()); +#endif field->widthValue( ) | rpl::start_with_next([=](int width) { @@ -454,10 +564,16 @@ void Options::Option::removePlaceholder() const { PollAnswer Options::Option::toPollAnswer(int index) const { Expects(index >= 0 && index < kMaxOptionsCount); + const auto text = field()->getTextWithTags(); + auto result = PollAnswer{ - field()->getLastText().trimmed(), - QByteArray(1, ('0' + index)) + TextWithEntities{ + .text = text.text, + .entities = TextUtilities::ConvertTextTagsToEntities(text.tags), + }, + QByteArray(1, ('0' + index)), }; + TextUtilities::Trim(result.text); result.correct = _correct ? _correct->entity()->Checkbox::checked() : false; return result; } @@ -467,13 +583,15 @@ rpl::producer Options::Option::removeClicks() const { } Options::Options( - not_null outer, + not_null box, not_null container, - not_null session, + not_null controller, + ChatHelpers::TabbedPanel *emojiPanel, bool chooseCorrectEnabled) -: _outer(outer) +: _box(box) , _container(container) -, _session(session) +, _controller(controller) +, _emojiPanel(emojiPanel) , _chooseCorrectGroup(chooseCorrectEnabled ? createChooseCorrectGroup() : nullptr) @@ -643,12 +761,40 @@ void Options::addEmptyOption() { (*(_list.end() - 2))->toggleRemoveAlways(true); } _list.push_back(std::make_unique