├── .editorconfig ├── .flatpak-exceptions.json ├── .flatpak-exceptions.json.license ├── .flatpak-manifest.json ├── .flatpak-manifest.json.license ├── .gitignore ├── .gitlab-ci.yml ├── .gitmodules ├── .kde-ci.yml ├── .mailmap ├── .markdownlint.yaml ├── .yamlignore ├── 3rdparty └── .clang-format ├── CMakeLists.txt ├── CMakePresets.json ├── CMakePresets.json.license ├── CONTRIBUTING.md ├── GlobalsGen.h.in ├── LICENSES ├── CC-BY-SA-4.0.txt ├── CC0-1.0.txt ├── GPL-3.0-or-later.txt ├── LGPL-2.1-or-later.txt └── MIT.txt ├── Messages.sh ├── NEWS.md ├── NEWS.md.license ├── README.md ├── data ├── data.qrc ├── images │ ├── chat-page-background.svg │ ├── chat-page-background.svg.license │ ├── images.qrc │ ├── kaidan-bw.svg │ ├── kaidan-bw.svg.license │ ├── kaidan.svg │ ├── kaidan.svg.license │ ├── onboarding │ │ ├── custom-form.svg │ │ ├── custom-form.svg.license │ │ ├── web-registration.svg │ │ └── web-registration.svg.license │ ├── qr-code-scan-1.svg │ ├── qr-code-scan-1.svg.license │ ├── qr-code-scan-2.svg │ ├── qr-code-scan-2.svg.license │ ├── qr-code-scan-own-1.svg │ ├── qr-code-scan-own-1.svg.license │ ├── qr-code-scan-own-2.svg │ └── qr-code-scan-own-2.svg.license ├── providers-completion.json ├── providers-completion.json.license ├── providers.json └── providers.json.license ├── kirigami-icons.qrc ├── misc ├── android │ ├── AndroidManifest.xml.in │ ├── build.gradle │ └── res │ │ ├── drawable │ │ ├── splash.xml │ │ └── splash.xml.license │ │ ├── mipmap-hdpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ ├── mipmap-ldpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ ├── mipmap-mdpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ ├── mipmap-xhdpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ ├── mipmap-xxhdpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ ├── mipmap-xxxhdpi │ │ ├── icon.png │ │ ├── icon.png.license │ │ ├── logo.png │ │ └── logo.png.license │ │ └── values │ │ ├── colors.xml │ │ ├── colors.xml.license │ │ ├── theme.xml │ │ └── theme.xml.license ├── app-icons │ ├── 16-kaidan.png │ ├── 16-kaidan.png.license │ ├── 256-kaidan.png │ ├── 256-kaidan.png.license │ ├── 32-kaidan.png │ ├── 32-kaidan.png.license │ ├── 48-kaidan.png │ └── 48-kaidan.png.license ├── doap.xml ├── im.kaidan.kaidan.appdata.xml ├── im.kaidan.kaidan.appdata.xml.license ├── im.kaidan.kaidan.desktop ├── ios │ ├── Info.plist.in │ └── Info.plist.in.license ├── kaidan-128x128.png ├── kaidan-128x128.png.license ├── kaidan.notifyrc ├── macos │ ├── Info.plist │ └── Info.plist.license ├── misc.qrc ├── notifications.qrc └── qtquickcontrols2.conf ├── poqm ├── ar │ └── kaidan_qt.po ├── ca │ └── kaidan_qt.po ├── ca@valencia │ └── kaidan_qt.po ├── cs │ └── kaidan_qt.po ├── de │ └── kaidan_qt.po ├── el │ └── kaidan_qt.po ├── en_GB │ └── kaidan_qt.po ├── eo │ └── kaidan_qt.po ├── es │ └── kaidan_qt.po ├── eu │ └── kaidan_qt.po ├── fi │ └── kaidan_qt.po ├── fr │ └── kaidan_qt.po ├── gl │ └── kaidan_qt.po ├── he │ └── kaidan_qt.po ├── hu │ └── kaidan_qt.po ├── hy │ └── kaidan_qt.po ├── ie │ └── kaidan_qt.po ├── it │ └── kaidan_qt.po ├── ja │ └── kaidan_qt.po ├── ka │ └── kaidan_qt.po ├── ko │ └── kaidan_qt.po ├── lt │ └── kaidan_qt.po ├── mr │ └── kaidan_qt.po ├── ms │ └── kaidan_qt.po ├── nb │ └── kaidan_qt.po ├── nl │ └── kaidan_qt.po ├── pa │ └── kaidan_qt.po ├── pl │ └── kaidan_qt.po ├── pt │ └── kaidan_qt.po ├── pt_BR │ └── kaidan_qt.po ├── ru │ └── kaidan_qt.po ├── sk │ └── kaidan_qt.po ├── sl │ └── kaidan_qt.po ├── sv │ └── kaidan_qt.po ├── tr │ └── kaidan_qt.po ├── uk │ └── kaidan_qt.po ├── zh_CN │ └── kaidan_qt.po └── zh_TW │ └── kaidan_qt.po ├── src ├── AbstractNotifier.h ├── AbstractQrCodeGenerator.cpp ├── AbstractQrCodeGenerator.h ├── Account.cpp ├── Account.h ├── AccountController.cpp ├── AccountController.h ├── AccountDb.cpp ├── AccountDb.h ├── AccountMigrationController.cpp ├── AccountMigrationController.h ├── AccountQrCodeGenerator.cpp ├── AccountQrCodeGenerator.h ├── AccountTrustMessageUriGenerator.cpp ├── AccountTrustMessageUriGenerator.h ├── Algorithms.h ├── AtmController.cpp ├── AtmController.h ├── AuthenticatableEncryptionKeyModel.cpp ├── AuthenticatableEncryptionKeyModel.h ├── AuthenticatedEncryptionKeyModel.cpp ├── AuthenticatedEncryptionKeyModel.h ├── AvatarFileStorage.cpp ├── AvatarFileStorage.h ├── BitsOfBinaryImageProvider.cpp ├── BitsOfBinaryImageProvider.h ├── Blocking.cpp ├── Blocking.h ├── CMakeLists.txt ├── ChatController.cpp ├── ChatController.h ├── ChatHintModel.cpp ├── ChatHintModel.h ├── ClientWorker.cpp ├── ClientWorker.h ├── ContactQrCodeGenerator.cpp ├── ContactQrCodeGenerator.h ├── ContactTrustMessageUriGenerator.cpp ├── ContactTrustMessageUriGenerator.h ├── CredentialsGenerator.cpp ├── CredentialsGenerator.h ├── CredentialsValidator.cpp ├── CredentialsValidator.h ├── DataFormModel.cpp ├── DataFormModel.h ├── Database.cpp ├── Database.h ├── DatabaseComponent.cpp ├── DatabaseComponent.h ├── DiscoveryManager.cpp ├── DiscoveryManager.h ├── EmojiModel.cpp ├── EmojiModel.h ├── Encryption.h ├── EncryptionController.cpp ├── EncryptionController.h ├── EncryptionKeyModel.cpp ├── EncryptionKeyModel.h ├── EncryptionWatcher.cpp ├── EncryptionWatcher.h ├── Enums.h ├── FileModel.cpp ├── FileModel.h ├── FileProgressCache.cpp ├── FileProgressCache.h ├── FileProxyModel.cpp ├── FileProxyModel.h ├── FileSharingController.cpp ├── FileSharingController.h ├── FutureUtils.h ├── Globals.h ├── GroupChatController.cpp ├── GroupChatController.h ├── GroupChatQrCodeGenerator.cpp ├── GroupChatQrCodeGenerator.h ├── GroupChatUser.cpp ├── GroupChatUser.h ├── GroupChatUserDb.cpp ├── GroupChatUserDb.h ├── GroupChatUserFilterModel.cpp ├── GroupChatUserFilterModel.h ├── GroupChatUserKeyAuthenticationFilterModel.cpp ├── GroupChatUserKeyAuthenticationFilterModel.h ├── GroupChatUserModel.cpp ├── GroupChatUserModel.h ├── GuiStyle.h ├── HostCompletionModel.cpp ├── HostCompletionModel.h ├── HostCompletionProxyModel.cpp ├── HostCompletionProxyModel.h ├── JsonUtils.h ├── Kaidan.cpp ├── Kaidan.h ├── LogHandler.cpp ├── LogHandler.h ├── LoginQrCodeGenerator.cpp ├── LoginQrCodeGenerator.h ├── MediaUtils.cpp ├── MediaUtils.h ├── Message.cpp ├── Message.h ├── MessageComposition.cpp ├── MessageComposition.h ├── MessageController.cpp ├── MessageController.h ├── MessageDb.cpp ├── MessageDb.h ├── MessageModel.cpp ├── MessageModel.h ├── MessageReactionModel.cpp ├── MessageReactionModel.h ├── MixController.cpp ├── MixController.h ├── Notifications.cpp ├── Notifications.h ├── OmemoController.cpp ├── OmemoController.h ├── OmemoDb.cpp ├── OmemoDb.h ├── PresenceCache.cpp ├── PresenceCache.h ├── ProviderListItem.cpp ├── ProviderListItem.h ├── ProviderListModel.cpp ├── ProviderListModel.h ├── PublicGroupChat.cpp ├── PublicGroupChat.h ├── PublicGroupChatModel.cpp ├── PublicGroupChatModel.h ├── PublicGroupChatProxyModel.cpp ├── PublicGroupChatProxyModel.h ├── PublicGroupChatSearchController.cpp ├── PublicGroupChatSearchController.h ├── QmlUtils.cpp ├── QmlUtils.h ├── RegistrationController.cpp ├── RegistrationController.h ├── RegistrationDataFormFilterModel.cpp ├── RegistrationDataFormFilterModel.h ├── RegistrationDataFormModel.cpp ├── RegistrationDataFormModel.h ├── RosterController.cpp ├── RosterController.h ├── RosterDb.cpp ├── RosterDb.h ├── RosterFilterProxyModel.cpp ├── RosterFilterProxyModel.h ├── RosterItem.cpp ├── RosterItem.h ├── RosterItemWatcher.cpp ├── RosterItemWatcher.h ├── RosterModel.cpp ├── RosterModel.h ├── ServerFeaturesCache.cpp ├── ServerFeaturesCache.h ├── Settings.cpp ├── Settings.h ├── SqlUtils.cpp ├── SqlUtils.h ├── StatusBar.cpp ├── StatusBar.h ├── SystemUtils.cpp ├── SystemUtils.h ├── TextFormatter.cpp ├── TextFormatter.h ├── TrustDb.cpp ├── TrustDb.h ├── TrustMessageUriGenerator.cpp ├── TrustMessageUriGenerator.h ├── UserDevicesModel.cpp ├── UserDevicesModel.h ├── VCardCache.cpp ├── VCardCache.h ├── VCardController.cpp ├── VCardController.h ├── VCardModel.cpp ├── VCardModel.h ├── VersionController.cpp ├── VersionController.h ├── XmlUtils.h ├── app │ ├── CMakeLists.txt │ └── main.cpp ├── qml │ ├── AboutDialog.qml │ ├── AboutPage.qml │ ├── ChatPage.qml │ ├── ChatPageBase.qml │ ├── EmptyChatPage.qml │ ├── GlobalDrawer.qml │ ├── ImageBackgroundPage.qml │ ├── QrCodeOnboardingPage.qml │ ├── RosterPage.qml │ ├── StartPage.qml │ ├── details │ │ ├── AccountDetailsContent.qml │ │ ├── AccountDetailsDialog.qml │ │ ├── AccountDetailsHeader.qml │ │ ├── AccountDetailsPage.qml │ │ ├── AccountKeyAuthenticationButton.qml │ │ ├── AvatarChangePage.qml │ │ ├── ContactDetailsContent.qml │ │ ├── ContactDetailsDialog.qml │ │ ├── ContactDetailsHeader.qml │ │ ├── ContactDetailsPage.qml │ │ ├── ContactKeyAuthenticationButton.qml │ │ ├── DetailsContent.qml │ │ ├── DetailsHeader.qml │ │ ├── GroupChatDetailsContent.qml │ │ ├── GroupChatDetailsDialog.qml │ │ ├── GroupChatDetailsHeader.qml │ │ ├── GroupChatDetailsPage.qml │ │ ├── GroupChatUserKeyAuthenticationButton.qml │ │ ├── KeyAuthenticationButton.qml │ │ ├── MediaOverview.qml │ │ ├── NotesChatDetailsContent.qml │ │ ├── NotesChatDetailsDialog.qml │ │ ├── NotesChatDetailsHeader.qml │ │ ├── NotesChatDetailsPage.qml │ │ ├── RosterItemDetailsContent.qml │ │ ├── RosterItemDetailsHeader.qml │ │ ├── UserDevicesArea.qml │ │ └── UserKeyAuthenticationButton.qml │ ├── elements │ │ ├── AboutContent.qml │ │ ├── AboutHeader.qml │ │ ├── AccountKeyAuthenticationPage.qml │ │ ├── AccountQrCode.qml │ │ ├── Audio.qml │ │ ├── Avatar.qml │ │ ├── BusyIndicatorFormButton.qml │ │ ├── Button.qml │ │ ├── CameraStatus.qml │ │ ├── CenteredAdaptiveButton.qml │ │ ├── CenteredAdaptiveHighlightedButton.qml │ │ ├── CenteredAdaptiveText.qml │ │ ├── ChatHintArea.qml │ │ ├── ChatInfo.qml │ │ ├── ChatMessage.qml │ │ ├── ChatMessageContextMenu.qml │ │ ├── ChatMessageContextMenuButton.qml │ │ ├── ChatPageSearchView.qml │ │ ├── ChatPageSendingPane.qml │ │ ├── ClickableIcon.qml │ │ ├── ClickableMouseArea.qml │ │ ├── ClickableText.qml │ │ ├── ConfirmationArea.qml │ │ ├── ConfirmationFormButtonArea.qml │ │ ├── ContactAdditionContent.qml │ │ ├── ContactAdditionDialog.qml │ │ ├── ContactAdditionPage.qml │ │ ├── ContactAdditionQrCodePage.qml │ │ ├── ContactKeyAuthenticationPage.qml │ │ ├── ContactQrCode.qml │ │ ├── CopyFormTextDelegate.qml │ │ ├── CustomConnectionSettings.qml │ │ ├── CustomContentFormCard.qml │ │ ├── DataForm.qml │ │ ├── Dialog.qml │ │ ├── EmojiPicker.qml │ │ ├── EncryptionKeysArea.qml │ │ ├── ExplainedContentPage.qml │ │ ├── ExplanationArea.qml │ │ ├── ExplanationOptionsTogglePage.qml │ │ ├── ExplanationTogglePage.qml │ │ ├── ExtendedMessageContent.qml │ │ ├── FormCardCustomContentArea.qml │ │ ├── FormComboBoxDelegate.qml │ │ ├── FormExpansionButton.qml │ │ ├── FormExpansionButtonBackground.qml │ │ ├── FormInfoContent.qml │ │ ├── FormInfoDialog.qml │ │ ├── FormInfoHeader.qml │ │ ├── FormInfoPage.qml │ │ ├── FormattedTextArea.qml │ │ ├── FormattedTextEdit.qml │ │ ├── GeoLocationMap.qml │ │ ├── GeoLocationMapPage.qml │ │ ├── GeoLocationMapPreview.qml │ │ ├── GeoLocationPreview.qml │ │ ├── GeoLocationSharingDialog.qml │ │ ├── GroupChatContactInvitationItem.qml │ │ ├── GroupChatCreationContent.qml │ │ ├── GroupChatCreationDialog.qml │ │ ├── GroupChatCreationPage.qml │ │ ├── GroupChatInvitation.qml │ │ ├── GroupChatJoiningContent.qml │ │ ├── GroupChatJoiningDialog.qml │ │ ├── GroupChatJoiningPage.qml │ │ ├── GroupChatParticipantPicker.qml │ │ ├── GroupChatQrCode.qml │ │ ├── GroupChatUserItem.qml │ │ ├── GroupChatUserKeyAuthenticationPage.qml │ │ ├── HighlightedFormButtonBackground.qml │ │ ├── HorizontalSeparator.qml │ │ ├── ImageCaptureDialog.qml │ │ ├── InlineListView.qml │ │ ├── KeyAuthenticationPage.qml │ │ ├── ListViewSearchField.qml │ │ ├── ListViewSectionDelegate.qml │ │ ├── LoadingArea.qml │ │ ├── LoadingStackArea.qml │ │ ├── LoginArea.qml │ │ ├── LoginQrCode.qml │ │ ├── MediumMessagePreview.qml │ │ ├── MediumPreview.qml │ │ ├── MediumSendingPreview.qml │ │ ├── MessageBackground.qml │ │ ├── MessageCounter.qml │ │ ├── MessageReactionAdditionButton.qml │ │ ├── MessageReactionButton.qml │ │ ├── MessageReactionDetailsButton.qml │ │ ├── MessageReactionDetailsDialog.qml │ │ ├── MessageReactionDisplayButton.qml │ │ ├── MessageReactionEmojiPicker.qml │ │ ├── MessageReactionRetryButton.qml │ │ ├── NewMediaDialog.qml │ │ ├── NonInteractiveFormDelegateBackground.qml │ │ ├── OpacityChangingMouseArea.qml │ │ ├── QrCode.qml │ │ ├── QrCodeScanner.qml │ │ ├── QrCodeScanningArea.qml │ │ ├── RecordingIndicator.qml │ │ ├── ReferencedMessage.qml │ │ ├── RosterFilteringArea.qml │ │ ├── RosterListItem.qml │ │ ├── RoundedImage.qml │ │ ├── RoundedRectangle.qml │ │ ├── ScalableText.qml │ │ ├── SearchBarPage.qml │ │ ├── SearchPublicGroupChatDialog.qml │ │ ├── SelectablePreview.qml │ │ ├── SelectionMarker.qml │ │ ├── ShutterRelease.qml │ │ ├── SimpleListViewSearchField.qml │ │ ├── Slider.qml │ │ ├── SliderBackground.qml │ │ ├── TextFieldCompleter.qml │ │ ├── UrlFormButtonDelegate.qml │ │ ├── UserKeyAuthenticationPage.qml │ │ ├── UserListItem.qml │ │ ├── UserListItemSelectionMarker.qml │ │ ├── VideoRecordingDialog.qml │ │ ├── ZoomSlider.qml │ │ └── fields │ │ │ ├── CredentialsField.qml │ │ │ ├── Field.qml │ │ │ ├── JidField.qml │ │ │ ├── PasswordField.qml │ │ │ └── RegistrationPasswordField.qml │ ├── main.qml │ ├── qml.qrc │ ├── qml.qrc.license │ └── registration │ │ ├── AutomaticRegistrationPage.qml │ │ ├── CustomDataFormArea.qml │ │ ├── ManualRegistrationPage.qml │ │ ├── ProviderPage.qml │ │ ├── RegistrationButton.qml │ │ ├── RegistrationPage.qml │ │ ├── RegistrationRequestPage.qml │ │ └── WebRegistrationPage.qml └── static_plugins.h ├── tests ├── CMakeLists.txt ├── DatabaseTest.cpp ├── FileModelTest.cpp ├── FutureUtilsTest.cpp ├── OmemoDbTest.cpp ├── PresenceCacheTest.cpp ├── PublicGroupChatTest.cpp ├── RosterItemWatcherTest.cpp ├── Test.cpp ├── Test.h ├── TestUtils.h ├── TrustDbTest.cpp └── manual │ ├── PublicGroupChatSearch.cpp │ └── SendFileSourcesAttachment.cpp └── utils ├── code-tidy.sh ├── optimize-png.sh ├── release.sh ├── render-graphics.sh ├── render-logos.sh ├── update-copyright-year.py ├── update-providers.sh └── update-submodule-breeze-icons.sh /.editorconfig: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Ilya Bizyaev 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | # EditorConfig file, https://EditorConfig.org 6 | 7 | # top-most EditorConfig file 8 | root = true 9 | 10 | # Unix-style newlines with a newline ending every file 11 | [*] 12 | end_of_line = lf 13 | trim_trailing_whitespace = true 14 | insert_final_newline = true 15 | charset = utf-8 16 | 17 | # Tab indentation (no size specified) 18 | [src/*.*] 19 | indent_style = tab 20 | 21 | [src/qml/**] 22 | indent_style = tab 23 | 24 | [utils/travis/**] 25 | indent_style = tab 26 | 27 | [*.py] 28 | indent_style = tab 29 | 30 | [*.plist] 31 | indent_style = tab 32 | 33 | [CMakeLists.txt] 34 | indent_style = tab 35 | 36 | # 4 space indentation 37 | [src/qxmpp-exts/**] 38 | indent_style = space 39 | indent_size = 4 40 | 41 | [src/singleapp/**] 42 | indent_style = space 43 | indent_size = 4 44 | 45 | [misc/android/{AndroidManifest.xml,build.gradle}] 46 | indent_style = space 47 | indent_size = 4 48 | 49 | [utils/build-*] 50 | indent_style = space 51 | indent_size = 4 52 | 53 | [clickable.json] 54 | indent_style = space 55 | indent_size = 2 56 | 57 | [*.qrc] 58 | indent_style = tab 59 | 60 | [*.pl] 61 | indent_style = space 62 | indent_size = 4 63 | -------------------------------------------------------------------------------- /.flatpak-exceptions.json: -------------------------------------------------------------------------------- 1 | { 2 | "im.kaidan.kaidan": [ 3 | "appid-filename-mismatch", 4 | "external-gitmodule-url-found", 5 | "module-libprotobuf-c-source-git-no-tag-commit-branch", 6 | "module-libomemo-c-source-git-branch" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.flatpak-exceptions.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /.flatpak-manifest.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2023 Plata Hill 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Jonah Brüchert 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | markdown: 6 | stage: validate 7 | image: 8 | name: davidanson/markdownlint-cli2:v0.14.0 9 | entrypoint: [""] 10 | script: markdownlint-cli2 "**/*.md" 11 | 12 | flatpak-appstream: 13 | stage: validate 14 | image: 15 | name: ghcr.io/flathub/flatpak-builder-lint:latest 16 | entrypoint: [""] 17 | script: flatpak-builder-lint appstream "misc/im.kaidan.kaidan.appdata.xml" 18 | 19 | flatpak-manifest: 20 | stage: validate 21 | image: 22 | name: ghcr.io/flathub/flatpak-builder-lint:latest 23 | entrypoint: [""] 24 | script: flatpak-builder-lint --exceptions --user-exceptions ".flatpak-exceptions.json" manifest ".flatpak-manifest.json" 25 | 26 | include: 27 | - project: sysadmin/ci-utilities 28 | file: 29 | - /gitlab-templates/json-validation.yml 30 | - /gitlab-templates/reuse-lint.yml 31 | - /gitlab-templates/xml-lint.yml 32 | - /gitlab-templates/yaml-lint.yml 33 | # - /gitlab-templates/android-qt6.yml 34 | - /gitlab-templates/freebsd-qt6.yml 35 | - /gitlab-templates/linux-qt6.yml 36 | - /gitlab-templates/linux-qt6-next.yml 37 | - /gitlab-templates/windows-qt6.yml 38 | - /gitlab-templates/clang-format.yml 39 | - /gitlab-templates/cppcheck.yml 40 | - /gitlab-templates/craft-windows-x86-64-qt6.yml 41 | - /gitlab-templates/craft-windows-appx-qt6.yml 42 | - /gitlab-templates/craft-macos-arm64-qt6.yml 43 | - /gitlab-templates/craft-macos-x86-64-qt6.yml 44 | - /gitlab-templates/flatpak.yml 45 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Ilya Bizyaev 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | [submodule "3rdparty/breeze-icons"] 6 | path = 3rdparty/breeze-icons 7 | url = https://invent.kde.org/frameworks/breeze-icons.git 8 | -------------------------------------------------------------------------------- /.kde-ci.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 Plata Hill 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | Dependencies: 6 | - 'on': ['@all'] 7 | require: 8 | frameworks/extra-cmake-modules: '@latest-kf6' 9 | frameworks/kcoreaddons: '@latest-kf6' 10 | frameworks/kio: '@latest-kf6' 11 | frameworks/kirigami: '@latest-kf6' 12 | frameworks/prison: '@latest-kf6' 13 | libraries/kirigami-addons: '@latest-kf6' 14 | libraries/kquickimageeditor: '@latest-kf6' 15 | libraries/qxmpp: '@latest-kf6' 16 | 'third-party/kdsingleapplication': '@latest-kf6' 17 | 18 | - 'on': ['Linux', 'FreeBSD', 'Windows', 'macOS'] 19 | require: 20 | 'frameworks/kcrash': '@latest-kf6' 21 | 'frameworks/kwindowsystem': '@latest-kf6' 22 | 23 | Options: 24 | require-passing-tests-on: ['@all'] 25 | clang-format-versions: [19] 26 | -------------------------------------------------------------------------------- /.mailmap: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Linus Jahn 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | # 6 | # This list is used by git-shortlog to fix a few botched name translations 7 | # in the git archive, either because the author's full name was messed up 8 | # and/or not always written the same way, making contributions from the 9 | # same person appearing not to be so. 10 | # 11 | 12 | Linus Jahn 13 | Linus Jahn 14 | Jonah Brüchert 15 | Jonah Brüchert 16 | Jonah Brüchert 17 | Jonah Brüchert 18 | geobra 19 | Muhammad Nur Hidayat Yasuyoshi 20 | Muhammad Nur Hidayat Yasuyoshi 21 | Muhammad Nur Hidayat Yasuyoshi 22 | Anonymous Anonymous _ 23 | Jeannette Lavoie 24 | Coucouf 25 | t4llkr 26 | marek 27 | Anna 28 | papadop 29 | ssantos 30 | TigranKhachatryan0 31 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | --- 6 | 7 | # line-length 8 | MD013: false 9 | -------------------------------------------------------------------------------- /.yamlignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | 3rdparty 6 | -------------------------------------------------------------------------------- /3rdparty/.clang-format: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | # Disable formatting in the directory of this file. 6 | DisableFormat: true 7 | SortIncludes: false 8 | -------------------------------------------------------------------------------- /CMakePresets.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2024 Laurent Montel 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /GlobalsGen.h.in: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Filipe Azevedo 2 | // SPDX-FileCopyrightText: 2025 Laurent Montel 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | #define BUILD_TYPE "${CMAKE_BUILD_TYPE}" 9 | 10 | #cmakedefine01 BUNDLE_ICONS 11 | #define TARGET_GSTREAMER_PLUGINS "${TARGET_GSTREAMER_PLUGINS}" 12 | 13 | #cmakedefine VERSION "${PROJECT_VERSION}" 14 | #cmakedefine VERSION_STRING "${VERSION_STRING}" 15 | 16 | #cmakedefine APPLICATION_ID "${APPLICATION_ID}" 17 | #cmakedefine APPLICATION_NAME "${APPLICATION_NAME}" 18 | #cmakedefine APPLICATION_DISPLAY_NAME "${APPLICATION_DISPLAY_NAME}" 19 | #cmakedefine APPLICATION_DESCRIPTION "${APPLICATION_DESCRIPTION}" 20 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Messages.sh: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 Albert Astals Cid 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | $EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kaidan_qt.pot 6 | -------------------------------------------------------------------------------- /NEWS.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Linus Jahn 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /data/data.qrc: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | providers.json 9 | providers-completion.json 10 | 11 | 12 | -------------------------------------------------------------------------------- /data/images/chat-page-background.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2022 Raghavendra Kamath 2 | 3 | SPDX-License-Identifier: CC-BY-SA-4.0 4 | -------------------------------------------------------------------------------- /data/images/images.qrc: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | kaidan.svg 9 | kaidan-bw.svg 10 | 11 | 12 | chat-page-background.svg 13 | kaidan.svg 14 | onboarding/custom-form.svg 15 | onboarding/web-registration.svg 16 | qr-code-scan-1.svg 17 | qr-code-scan-2.svg 18 | qr-code-scan-own-1.svg 19 | qr-code-scan-own-2.svg 20 | 21 | 22 | -------------------------------------------------------------------------------- /data/images/kaidan-bw.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /data/images/kaidan-bw.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /data/images/kaidan.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /data/images/onboarding/custom-form.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 2 | 3 | SPDX-License-Identifier: CC-BY-SA-4.0 4 | -------------------------------------------------------------------------------- /data/images/onboarding/web-registration.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 2 | 3 | SPDX-License-Identifier: CC-BY-SA-4.0 4 | -------------------------------------------------------------------------------- /data/images/qr-code-scan-1.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Mathis Brüchert 2 | 3 | SPDX-License-Identifier: CC-BY-SA-4.0 4 | -------------------------------------------------------------------------------- /data/images/qr-code-scan-2.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Mathis Brüchert 2 | 3 | SPDX-License-Identifier: CC-BY-SA-4.0 4 | -------------------------------------------------------------------------------- /data/images/qr-code-scan-own-1.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Mathis Brüchert 2 | SPDX-FileCopyrightText: 2022 Melvin Keskin 3 | 4 | SPDX-License-Identifier: CC-BY-SA-4.0 5 | -------------------------------------------------------------------------------- /data/images/qr-code-scan-own-2.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Mathis Brüchert 2 | SPDX-FileCopyrightText: 2022 Melvin Keskin 3 | 4 | SPDX-License-Identifier: CC-BY-SA-4.0 5 | -------------------------------------------------------------------------------- /data/providers-completion.json: -------------------------------------------------------------------------------- 1 | [ 2 | "07f.de", 3 | "404.city", 4 | "5222.de", 5 | "a3.pm", 6 | "anoxinon.me", 7 | "ari.lt", 8 | "blah.im", 9 | "canchat.org", 10 | "chalec.org", 11 | "chapril.org", 12 | "chat.between-us.online", 13 | "chat.sum7.eu", 14 | "chatrix.one", 15 | "chatterboxtown.us", 16 | "chinwag.im", 17 | "citw.lgbt", 18 | "conversations.im", 19 | "decent.im", 20 | "dismail.de", 21 | "disroot.org", 22 | "draugr.de", 23 | "eigenlab.org", 24 | "elaon.de", 25 | "esiliati.org", 26 | "gnu.gr", 27 | "hookipa.net", 28 | "jabber-germany.de", 29 | "jabber.5july.net", 30 | "jabber.at", 31 | "jabber.calyxinstitute.org", 32 | "jabber.cat", 33 | "jabber.chaostreffbern.ch", 34 | "jabber.de", 35 | "jabber.fr", 36 | "jabber.hot-chilli.net", 37 | "jabber.no", 38 | "jabber.org", 39 | "jabber.ru", 40 | "jabber.systemausfall.org", 41 | "jabber.vg", 42 | "jabber.weimarnetz.de", 43 | "jabbers.one", 44 | "jabbim.com", 45 | "jabbxi.de", 46 | "jabjab.de", 47 | "jix.im", 48 | "laberzentrale.de", 49 | "liberta.casa", 50 | "libre-chat.net", 51 | "lightwitch.org", 52 | "macaw.me", 53 | "magicbroccoli.de", 54 | "mailbox.org", 55 | "monocles.eu", 56 | "movim.eu", 57 | "nixnet.services", 58 | "pimux.de", 59 | "potato.mx", 60 | "projectsegfau.lt", 61 | "redlibre.es", 62 | "step.im", 63 | "suchat.org", 64 | "sure.im", 65 | "tchncs.de", 66 | "trashserver.net", 67 | "trung.fun", 68 | "worlio.com", 69 | "xmpp.earth", 70 | "xmpp.eco.br", 71 | "xmpp.is", 72 | "xmpp.jp", 73 | "xmpp.party", 74 | "yax.im", 75 | "yourdata.forsale" 76 | ] 77 | -------------------------------------------------------------------------------- /data/providers-completion.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /data/providers.json.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Linus Jahn 2 | SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | 4 | SPDX-License-Identifier: CC0-1.0 5 | -------------------------------------------------------------------------------- /misc/android/res/drawable/splash.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /misc/android/res/drawable/splash.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2017 Ilya Bizyaev 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-hdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-hdpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-hdpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-hdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-hdpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-hdpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-ldpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-ldpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-ldpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-ldpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-ldpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-ldpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-mdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-mdpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-mdpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-mdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-mdpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-mdpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xhdpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xhdpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xhdpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xhdpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xxhdpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxhdpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xxhdpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxhdpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxxhdpi/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xxxhdpi/icon.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxxhdpi/icon.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/android/res/mipmap-xxxhdpi/logo.png -------------------------------------------------------------------------------- /misc/android/res/mipmap-xxxhdpi/logo.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/android/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FF4CAF50 4 | #FF009688 5 | #FF009688 6 | 7 | -------------------------------------------------------------------------------- /misc/android/res/values/colors.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2017 Ilya Bizyaev 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/android/res/values/theme.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /misc/android/res/values/theme.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2017 Ilya Bizyaev 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/app-icons/16-kaidan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/app-icons/16-kaidan.png -------------------------------------------------------------------------------- /misc/app-icons/16-kaidan.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/app-icons/256-kaidan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/app-icons/256-kaidan.png -------------------------------------------------------------------------------- /misc/app-icons/256-kaidan.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/app-icons/32-kaidan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/app-icons/32-kaidan.png -------------------------------------------------------------------------------- /misc/app-icons/32-kaidan.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/app-icons/48-kaidan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/app-icons/48-kaidan.png -------------------------------------------------------------------------------- /misc/app-icons/48-kaidan.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/im.kaidan.kaidan.appdata.xml.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Jonah Brüchert 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/ios/Info.plist.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | Kaidan 7 | CFBundleExecutable 8 | kaidan 9 | CFBundleGetInfoString 10 | Created by Qt/QMake 11 | CFBundleIconFile 12 | kaidan.icns 13 | CFBundleIdentifier 14 | @APPLICATION_ID@ 15 | CFBundleName 16 | @APPLICATION_NAME@ 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | @VERSION_CODE@ 21 | CFBundleSignature 22 | 23 | CFBundleVersion 24 | @VERSION_STRING@ 25 | LSRequiresIPhoneOS 26 | 27 | MinimumOSVersion 28 | 10.0 29 | UILaunchStoryboardName 30 | LaunchScreen 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationPortraitUpsideDown 35 | UIInterfaceOrientationLandscapeLeft 36 | UIInterfaceOrientationLandscapeRight 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /misc/ios/Info.plist.in.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Ilya Bizyaev 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/kaidan-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KDE/kaidan/48063a7d1cff0e1395c50e7c5b2d4a5a038794a3/misc/kaidan-128x128.png -------------------------------------------------------------------------------- /misc/kaidan-128x128.png.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 Ilya Bizyaev 2 | SPDX-FileCopyrightText: 2020 Mathis Brüchert 3 | SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | 5 | SPDX-License-Identifier: CC-BY-SA-4.0 6 | -------------------------------------------------------------------------------- /misc/macos/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | kaidan 9 | CFBundleGetInfoString 10 | 11 | CFBundleIconFile 12 | kaidan 13 | CFBundleIdentifier 14 | im.kaidan.kaidan 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleLongVersionString 18 | 19 | CFBundleName 20 | 21 | CFBundlePackageType 22 | APPL 23 | CFBundleShortVersionString 24 | 25 | CFBundleSignature 26 | ???? 27 | CFBundleVersion 28 | 29 | CSResourcesFileMapped 30 | 31 | NSHumanReadableCopyright 32 | 33 | NSPrincipalClass 34 | NSApplication 35 | NSHighResolutionCapable 36 | True 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /misc/macos/Info.plist.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Ilya Bizyaev 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /misc/misc.qrc: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | qtquickcontrols2.conf 9 | 10 | 11 | -------------------------------------------------------------------------------- /misc/notifications.qrc: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | kaidan.notifyrc 9 | 10 | 11 | -------------------------------------------------------------------------------- /misc/qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Linus Jahn 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | [Universal] 6 | Theme=System 7 | Accent=Green 8 | 9 | [Material] 10 | Theme=Light 11 | Accent=Green 12 | Primary=Red 13 | 14 | -------------------------------------------------------------------------------- /src/AbstractQrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2020 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | #pragma once 8 | 9 | // Qt 10 | #include 11 | 12 | class QImage; 13 | 14 | class AbstractQrCodeGenerator : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | Q_PROPERTY(QString jid READ jid WRITE setJid NOTIFY jidChanged) 19 | Q_PROPERTY(int edgePixelCount MEMBER m_edgePixelCount WRITE setEdgePixelCount) 20 | Q_PROPERTY(QImage qrCode READ qrCode NOTIFY qrCodeChanged) 21 | 22 | public: 23 | explicit AbstractQrCodeGenerator(QObject *parent = nullptr); 24 | 25 | QString jid() const; 26 | void setJid(const QString &jid); 27 | Q_SIGNAL void jidChanged(); 28 | 29 | void setEdgePixelCount(int edgePixelCount); 30 | 31 | QImage qrCode() const; 32 | Q_SIGNAL void qrCodeChanged(); 33 | 34 | protected: 35 | void setText(const QString &text); 36 | 37 | private: 38 | QString m_jid; 39 | 40 | // Text to be encoded as a QR code. 41 | QString m_text; 42 | 43 | // Number of pixels as the width and height of the QR code. 44 | int m_edgePixelCount; 45 | }; 46 | -------------------------------------------------------------------------------- /src/Account.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "Account.h" 6 | 7 | // QXmpp 8 | #include 9 | 10 | QString Account::displayName() const 11 | { 12 | if (!name.isEmpty()) { 13 | return name; 14 | } 15 | 16 | return QXmppUtils::jidToUser(jid); 17 | } 18 | 19 | QString Account::jidResource() const 20 | { 21 | if (resourcePrefix.isEmpty()) { 22 | m_jidResource.clear(); 23 | return {}; 24 | } 25 | 26 | if (m_jidResource.isEmpty()) { 27 | m_jidResource = resourcePrefix % QLatin1Char('.') % QXmppUtils::generateStanzaHash(4); 28 | } 29 | 30 | return {}; 31 | } 32 | 33 | bool Account::isDefaultPort() const 34 | { 35 | return port == PORT_AUTODETECT; 36 | } 37 | 38 | bool Account::operator<(const Account &other) const 39 | { 40 | return jid < other.jid; 41 | } 42 | 43 | bool Account::operator>(const Account &other) const 44 | { 45 | return jid > other.jid; 46 | } 47 | 48 | bool Account::operator<=(const Account &other) const 49 | { 50 | return jid <= other.jid; 51 | } 52 | 53 | bool Account::operator>=(const Account &other) const 54 | { 55 | return jid >= other.jid; 56 | } 57 | 58 | #include "moc_Account.cpp" 59 | -------------------------------------------------------------------------------- /src/AccountDb.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2024 Filipe Azevedo 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Kaidan 9 | #include "DatabaseComponent.h" 10 | 11 | class Account; 12 | 13 | class AccountDb : public DatabaseComponent 14 | { 15 | Q_OBJECT 16 | 17 | friend class Database; 18 | 19 | public: 20 | AccountDb(Database *db, QObject *parent = nullptr); 21 | ~AccountDb(); 22 | 23 | static AccountDb *instance(); 24 | 25 | QFuture addAccount(const QString &jid); 26 | QFuture account(const QString &jid); 27 | QFuture lastAccount(); 28 | QFuture updateAccount(const QString &jid, const std::function &updateAccount); 29 | Q_SIGNAL void accountUpdated(const Account &account); 30 | 31 | /** 32 | * Fetches the stanza ID of the latest locally stored (existing or removed) message. 33 | */ 34 | QFuture fetchLatestMessageStanzaId(const QString &jid); 35 | 36 | QFuture removeAccount(const QString &jid); 37 | 38 | private: 39 | static void parseAccountsFromQuery(QSqlQuery &query, QList &accounts); 40 | static QSqlRecord createUpdateRecord(const Account &oldAccount, const Account &newAccount); 41 | void updateAccountByRecord(const QString &jid, const QSqlRecord &record); 42 | 43 | static AccountDb *s_instance; 44 | }; 45 | -------------------------------------------------------------------------------- /src/AccountQrCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "AccountQrCodeGenerator.h" 6 | 7 | AccountQrCodeGenerator::AccountQrCodeGenerator(QObject *parent) 8 | : AbstractQrCodeGenerator(parent) 9 | { 10 | connect(this, &AccountQrCodeGenerator::jidChanged, this, &AccountQrCodeGenerator::updateUri); 11 | connect(&m_uriGenerator, &TrustMessageUriGenerator::uriChanged, this, &AccountQrCodeGenerator::updateText); 12 | } 13 | 14 | void AccountQrCodeGenerator::updateUri() 15 | { 16 | m_uriGenerator.setJid(jid()); 17 | } 18 | 19 | void AccountQrCodeGenerator::updateText() 20 | { 21 | setText(m_uriGenerator.uri()); 22 | } 23 | 24 | #include "moc_AccountQrCodeGenerator.cpp" 25 | -------------------------------------------------------------------------------- /src/AccountQrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "AbstractQrCodeGenerator.h" 9 | #include "AccountTrustMessageUriGenerator.h" 10 | 11 | /** 12 | * Gerenates a QR code encoding the Trust Message URI of an account. 13 | * 14 | * If no keys for the Trust Message URI can be found, an XMPP URI containing only the account's bare 15 | * JID is used. 16 | */ 17 | class AccountQrCodeGenerator : public AbstractQrCodeGenerator 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit AccountQrCodeGenerator(QObject *parent = nullptr); 23 | 24 | private: 25 | void updateUri(); 26 | void updateText(); 27 | 28 | AccountTrustMessageUriGenerator m_uriGenerator; 29 | }; 30 | -------------------------------------------------------------------------------- /src/AccountTrustMessageUriGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "TrustMessageUriGenerator.h" 9 | 10 | class AccountTrustMessageUriGenerator : public TrustMessageUriGenerator 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit AccountTrustMessageUriGenerator(QObject *parent = nullptr); 16 | 17 | private: 18 | void setUp(); 19 | void handleKeysChanged(const QString &accountJid, const QList &jids); 20 | void updateKeys(); 21 | }; 22 | -------------------------------------------------------------------------------- /src/AtmController.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | 11 | class Database; 12 | class QXmppClient; 13 | class QXmppAtmManager; 14 | class QXmppUri; 15 | class TrustDb; 16 | 17 | class AtmController : public QObject 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | AtmController(QObject *parent = nullptr); 23 | ~AtmController(); 24 | 25 | /** 26 | * Sets the JID of the current account used to store the corresponding data 27 | * for a specific account. 28 | * 29 | * @param accountJid bare JID of the current account 30 | */ 31 | void setAccountJid(const QString &accountJid); 32 | 33 | /** 34 | * Authenticates or distrusts end-to-end encryption keys by a given XMPP URI 35 | * (e.g., from a scanned QR code). 36 | * 37 | * @param uri Trust Message URI 38 | */ 39 | void makeTrustDecisionsByUri(const QXmppUri &uri); 40 | Q_INVOKABLE void makeTrustDecisions(const QString &jid, const QList &keyIdsForAuthentication, const QList &keyIdsForDistrusting); 41 | 42 | private: 43 | static QList keyIdsFromHex(const QList &keyIds); 44 | 45 | QXmppAtmManager *const m_manager; 46 | }; 47 | -------------------------------------------------------------------------------- /src/AuthenticatableEncryptionKeyModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "EncryptionKeyModel.h" 9 | 10 | class AuthenticatableEncryptionKeyModel : public EncryptionKeyModel 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString chatJid READ chatJid WRITE setChatJid NOTIFY chatJidChanged) 15 | 16 | public: 17 | explicit AuthenticatableEncryptionKeyModel(QObject *parent = nullptr); 18 | ~AuthenticatableEncryptionKeyModel() override; 19 | 20 | [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override; 21 | [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 22 | 23 | QString chatJid() const; 24 | void setChatJid(const QString &chatJid); 25 | Q_SIGNAL void chatJidChanged(); 26 | 27 | Q_INVOKABLE bool contains(const QString &keyId); 28 | 29 | protected: 30 | virtual void setUp() override; 31 | 32 | private: 33 | void handleDevicesChanged(const QString &accountJid, QList jids); 34 | void updateKeys(); 35 | 36 | QString m_chatJid; 37 | }; 38 | -------------------------------------------------------------------------------- /src/AuthenticatedEncryptionKeyModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "EncryptionKeyModel.h" 9 | 10 | class AuthenticatedEncryptionKeyModel : public EncryptionKeyModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit AuthenticatedEncryptionKeyModel(QObject *parent = nullptr); 16 | ~AuthenticatedEncryptionKeyModel() override; 17 | 18 | [[nodiscard]] int rowCount(const QModelIndex &parent = {}) const override; 19 | [[nodiscard]] QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 20 | 21 | protected: 22 | void setUp() override; 23 | 24 | private: 25 | void handleOwnDeviceChanged(const QString &accountJid); 26 | void handleDevicesChanged(const QString &accountJid, const QList &jids); 27 | 28 | void updateOwnKey(); 29 | void updateKeys(); 30 | 31 | Key m_ownKey; 32 | }; 33 | -------------------------------------------------------------------------------- /src/ContactQrCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "ContactQrCodeGenerator.h" 6 | 7 | // Qt 8 | #include 9 | 10 | ContactQrCodeGenerator::ContactQrCodeGenerator(QObject *parent) 11 | : AbstractQrCodeGenerator(parent) 12 | { 13 | connect(this, &ContactQrCodeGenerator::jidChanged, this, &ContactQrCodeGenerator::updateUriJid); 14 | connect(&m_uriGenerator, &TrustMessageUriGenerator::uriChanged, this, &ContactQrCodeGenerator::updateText); 15 | } 16 | 17 | QString ContactQrCodeGenerator::accountJid() const 18 | { 19 | return m_accountJid; 20 | } 21 | 22 | void ContactQrCodeGenerator::setAccountJid(const QString &accountJid) 23 | { 24 | if (m_accountJid != accountJid) { 25 | m_accountJid = accountJid; 26 | Q_EMIT accountJidChanged(); 27 | updateUriAccountJid(); 28 | } 29 | } 30 | 31 | void ContactQrCodeGenerator::updateUriAccountJid() 32 | { 33 | m_uriGenerator.setAccountJid(m_accountJid); 34 | } 35 | 36 | void ContactQrCodeGenerator::updateUriJid() 37 | { 38 | m_uriGenerator.setJid(jid()); 39 | } 40 | 41 | void ContactQrCodeGenerator::updateText() 42 | { 43 | setText(m_uriGenerator.uri()); 44 | } 45 | 46 | #include "moc_ContactQrCodeGenerator.cpp" 47 | -------------------------------------------------------------------------------- /src/ContactQrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "AbstractQrCodeGenerator.h" 9 | #include "ContactTrustMessageUriGenerator.h" 10 | 11 | /** 12 | * Gerenates a QR code encoding the Trust Message URI of a contact. 13 | * 14 | * If no keys for the Trust Message URI can be found, an XMPP URI containing only the contact's bare 15 | * JID is used. 16 | */ 17 | class ContactQrCodeGenerator : public AbstractQrCodeGenerator 18 | { 19 | Q_OBJECT 20 | 21 | Q_PROPERTY(QString accountJid READ accountJid WRITE setAccountJid NOTIFY accountJidChanged) 22 | 23 | public: 24 | explicit ContactQrCodeGenerator(QObject *parent = nullptr); 25 | 26 | QString accountJid() const; 27 | void setAccountJid(const QString &accountJid); 28 | Q_SIGNAL void accountJidChanged(); 29 | 30 | private: 31 | void updateUriAccountJid(); 32 | void updateUriJid(); 33 | void updateText(); 34 | 35 | QString m_accountJid; 36 | ContactTrustMessageUriGenerator m_uriGenerator; 37 | }; 38 | -------------------------------------------------------------------------------- /src/ContactTrustMessageUriGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "TrustMessageUriGenerator.h" 9 | 10 | class ContactTrustMessageUriGenerator : public TrustMessageUriGenerator 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString accountJid MEMBER m_accountJid WRITE setAccountJid) 15 | 16 | public: 17 | explicit ContactTrustMessageUriGenerator(QObject *parent = nullptr); 18 | 19 | void setAccountJid(const QString &accountJid); 20 | 21 | private: 22 | void handleJidChanged(); 23 | void setUp(); 24 | void handleKeysChanged(const QString &accountJid, const QList &jids); 25 | void updateKeys(); 26 | 27 | QString m_accountJid; 28 | }; 29 | -------------------------------------------------------------------------------- /src/CredentialsGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | #include 9 | 10 | /** 11 | * This class contains generators for usernames and passwords. 12 | */ 13 | class CredentialsGenerator : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit CredentialsGenerator(QObject *parent = nullptr); 19 | 20 | /** 21 | * Generates a random string with alternating consonants and vowels. 22 | * 23 | * Whether the string starts with a consonant or vowel is random. 24 | */ 25 | Q_INVOKABLE static QString generatePronounceableName(unsigned int length); 26 | 27 | /** 28 | * Generates a pronounceable username with @c GENERATED_USERNAME_LENGTH as fixed length. 29 | */ 30 | Q_INVOKABLE static QString generateUsername(); 31 | 32 | /** 33 | * Generates a random password containing characters from 34 | * @c GENERATED_PASSWORD_ALPHABET with a length between 35 | * @c GENERATED_PASSWORD_LOWER_BOUND (including) and 36 | * @c GENERATED_PASSWORD_UPPER_BOUND (including). 37 | */ 38 | Q_INVOKABLE static QString generatePassword(); 39 | 40 | /** 41 | * Generates a random password containing characters from 42 | * @c GENERATED_PASSWORD_ALPHABET. 43 | */ 44 | Q_INVOKABLE static QString generatePassword(unsigned int length); 45 | }; 46 | -------------------------------------------------------------------------------- /src/CredentialsValidator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2020 Linus Jahn 3 | // SPDX-FileCopyrightText: 2021 Jonah Brüchert 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | #include "CredentialsValidator.h" 8 | 9 | // QXmpp 10 | #include 11 | 12 | CredentialsValidator::CredentialsValidator(QObject *parent) 13 | : QObject(parent) 14 | { 15 | } 16 | 17 | bool CredentialsValidator::isUserJidValid(const QString &jid) 18 | { 19 | return jid.count(QLatin1Char('@')) == 1 && isUsernameValid(QXmppUtils::jidToUser(jid)) && isServerValid(QXmppUtils::jidToDomain(jid)); 20 | } 21 | 22 | bool CredentialsValidator::isUsernameValid(const QString &username) 23 | { 24 | return !(username.isEmpty() || username.contains(u' ')); 25 | } 26 | 27 | bool CredentialsValidator::isServerValid(const QString &server) 28 | { 29 | return !(server.isEmpty() || server.contains(u' ')); 30 | } 31 | 32 | bool CredentialsValidator::isPasswordValid(const QString &password) 33 | { 34 | return !password.isEmpty(); 35 | } 36 | 37 | #include "moc_CredentialsValidator.cpp" 38 | -------------------------------------------------------------------------------- /src/CredentialsValidator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2020 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | 11 | /** 12 | * This is a validator for XMPP account credentials. 13 | */ 14 | class CredentialsValidator : public QObject 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit CredentialsValidator(QObject *parent = nullptr); 20 | 21 | /** 22 | * Returns true if the given string is a valid JID of an XMPP user. 23 | * 24 | * @param jid JID to be validated 25 | */ 26 | Q_INVOKABLE static bool isUserJidValid(const QString &jid); 27 | 28 | /** 29 | * Returns true if the given string is a valid username. 30 | * 31 | * @param username username to be validated 32 | */ 33 | Q_INVOKABLE static bool isUsernameValid(const QString &username); 34 | 35 | /** 36 | * Returns true if the given string is a valid server. 37 | * 38 | * @param username server to be validated 39 | */ 40 | Q_INVOKABLE static bool isServerValid(const QString &server); 41 | 42 | /** 43 | * Returns true if the given string is a valid password. 44 | * 45 | * @param password password to be validated 46 | */ 47 | Q_INVOKABLE static bool isPasswordValid(const QString &password); 48 | }; 49 | -------------------------------------------------------------------------------- /src/DatabaseComponent.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Linus Jahn 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | // Kaidan 10 | #include "FutureUtils.h" 11 | #include "SqlUtils.h" 12 | 13 | class QSqlQuery; 14 | class QSqlDriver; 15 | class QSqlRecord; 16 | class Database; 17 | 18 | class DatabaseComponent : public QObject 19 | { 20 | Q_OBJECT 21 | public: 22 | DatabaseComponent(Database *database, QObject *parent = nullptr); 23 | 24 | void insert(const QString &tableName, const SqlUtils::QueryBindValues &values); 25 | 26 | // Inserts a row that may contain binary fields (e.g., QByteArray) into a table. 27 | void insertBinary(const QString &tableName, const SqlUtils::QueryBindValues &values); 28 | 29 | QSqlQuery createQuery(); 30 | QSqlDriver &sqlDriver(); 31 | QSqlRecord sqlRecord(const QString &tableName); 32 | void transaction(); 33 | void commit(); 34 | 35 | template 36 | auto run(Functor function) const 37 | { 38 | return runAsync(dbWorker(), function); 39 | } 40 | 41 | protected: 42 | QObject *dbWorker() const; 43 | 44 | private: 45 | Database *const m_database; 46 | }; 47 | -------------------------------------------------------------------------------- /src/DiscoveryManager.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2017 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // QXmpp 11 | #include 12 | #include 13 | 14 | class QXmppDiscoveryManager; 15 | 16 | /** 17 | * @class DiscoveryManager Manager for outgoing/incoming service discovery requests and results 18 | * 19 | * XEP-0030: Service Discovery (https://xmpp.org/extensions/xep-0030.html) 20 | */ 21 | class DiscoveryManager : public QObject 22 | { 23 | public: 24 | explicit DiscoveryManager(QXmppClient *client, QXmppDiscoveryManager *discoveryManager, QObject *parent = nullptr); 25 | 26 | ~DiscoveryManager(); 27 | 28 | private: 29 | /** 30 | * Requests disco info and items from the server. 31 | * The results are used, for example, by QXmppMixManager. 32 | */ 33 | void requestData(); 34 | 35 | /** 36 | * Handles incoming results of disco item requests 37 | */ 38 | void handleItems(QList &&); 39 | 40 | QXmppClient *const m_client; 41 | QXmppDiscoveryManager *const m_manager; 42 | }; 43 | -------------------------------------------------------------------------------- /src/Encryption.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // QXmpp 11 | #include 12 | 13 | class Encryption 14 | { 15 | Q_GADGET 16 | 17 | public: 18 | enum Enum { 19 | NoEncryption = QXmpp::NoEncryption, ///< No encryption 20 | Omemo2 = QXmpp::Omemo2 ///< XEP-0384 OMEMO Encryption since version 0.8 21 | }; 22 | Q_ENUM(Enum) 23 | }; 24 | -------------------------------------------------------------------------------- /src/EncryptionKeyModel.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "EncryptionKeyModel.h" 6 | 7 | EncryptionKeyModel::EncryptionKeyModel(QObject *parent) 8 | : QAbstractListModel(parent) 9 | { 10 | } 11 | 12 | EncryptionKeyModel::~EncryptionKeyModel() 13 | { 14 | } 15 | 16 | QHash EncryptionKeyModel::roleNames() const 17 | { 18 | static const QHash roles = { 19 | {static_cast(Role::Label), QByteArrayLiteral("label")}, 20 | {static_cast(Role::KeyId), QByteArrayLiteral("keyId")}, 21 | }; 22 | 23 | return roles; 24 | } 25 | 26 | QString EncryptionKeyModel::accountJid() const 27 | { 28 | return m_accountJid; 29 | } 30 | 31 | void EncryptionKeyModel::setAccountJid(const QString &accountJid) 32 | { 33 | if (m_accountJid != accountJid) { 34 | m_accountJid = accountJid; 35 | Q_EMIT accountJidChanged(); 36 | 37 | setUp(); 38 | } 39 | } 40 | 41 | #include "moc_EncryptionKeyModel.cpp" 42 | -------------------------------------------------------------------------------- /src/EncryptionKeyModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class EncryptionKeyModel : public QAbstractListModel 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString accountJid READ accountJid WRITE setAccountJid NOTIFY accountJidChanged) 15 | 16 | public: 17 | enum class Role { 18 | Label = Qt::DisplayRole, 19 | KeyId, 20 | }; 21 | 22 | struct Key { 23 | QString deviceLabel; 24 | QString id; 25 | }; 26 | 27 | explicit EncryptionKeyModel(QObject *parent = nullptr); 28 | ~EncryptionKeyModel() override; 29 | 30 | [[nodiscard]] QHash roleNames() const override; 31 | 32 | QString accountJid() const; 33 | void setAccountJid(const QString &accountJid); 34 | Q_SIGNAL void accountJidChanged(); 35 | 36 | protected: 37 | virtual void setUp() = 0; 38 | 39 | QList m_keys; 40 | 41 | private: 42 | QString m_accountJid; 43 | }; 44 | -------------------------------------------------------------------------------- /src/FileModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | #include 10 | // Kaidan 11 | #include "Message.h" 12 | 13 | using Files = QList; 14 | 15 | class FileModel : public QAbstractListModel 16 | { 17 | Q_OBJECT 18 | 19 | Q_PROPERTY(QString accountJid READ accountJid WRITE setAccountJid NOTIFY accountJidChanged) 20 | Q_PROPERTY(QString chatJid READ chatJid WRITE setChatJid NOTIFY chatJidChanged) 21 | Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged) 22 | 23 | public: 24 | enum class Role { 25 | Id = Qt::UserRole, 26 | File, 27 | Thumbnail, 28 | }; 29 | Q_ENUM(Role) 30 | 31 | explicit FileModel(QObject *parent = nullptr); 32 | ~FileModel() override; 33 | 34 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 35 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 36 | QHash roleNames() const override; 37 | 38 | QString accountJid() const; 39 | void setAccountJid(const QString &jid); 40 | Q_SIGNAL void accountJidChanged(const QString &jid); 41 | 42 | QString chatJid() const; 43 | void setChatJid(const QString &jid); 44 | Q_SIGNAL void chatJidChanged(const QString &jid); 45 | 46 | Files files() const; 47 | void setFiles(const Files &files); 48 | 49 | Q_SLOT void loadFiles(); 50 | Q_SLOT void loadDownloadedFiles(); 51 | 52 | Q_SIGNAL void rowCountChanged(); 53 | 54 | private: 55 | QString m_accountJid; 56 | QString m_chatJid; 57 | Files m_files; 58 | QFutureWatcher m_watcher; 59 | }; 60 | 61 | Q_DECLARE_METATYPE(FileModel::Role) 62 | -------------------------------------------------------------------------------- /src/FileSharingController.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2022 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // QXmpp 11 | #include 12 | #include 13 | 14 | struct File; 15 | class QXmppClient; 16 | 17 | class FileSharingController : public QObject 18 | { 19 | Q_OBJECT 20 | public: 21 | using SendFilesResult = std::variant, QXmppError>; 22 | using UploadResult = std::tuple; 23 | 24 | explicit FileSharingController(QXmppClient *client); 25 | 26 | auto sendFiles(QList files, bool encrypt) -> QXmppTask; 27 | Q_INVOKABLE void downloadFile(const QString &messageId, const File &file); 28 | Q_INVOKABLE void deleteFile(const QString &messageId, const File &file); 29 | 30 | Q_SIGNAL void errorOccured(qint64, QXmppError); 31 | 32 | private: 33 | QFuture sendFile(const File &file, bool encrypt); 34 | }; 35 | -------------------------------------------------------------------------------- /src/GroupChatQrCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "GroupChatQrCodeGenerator.h" 6 | 7 | // QXmpp 8 | #include 9 | 10 | GroupChatQrCodeGenerator::GroupChatQrCodeGenerator(QObject *parent) 11 | : AbstractQrCodeGenerator(parent) 12 | { 13 | connect(this, &GroupChatQrCodeGenerator::jidChanged, this, &GroupChatQrCodeGenerator::updateText); 14 | } 15 | 16 | void GroupChatQrCodeGenerator::updateText() 17 | { 18 | QXmppUri uri; 19 | 20 | uri.setJid(jid()); 21 | uri.setQuery(QXmpp::Uri::Join()); 22 | 23 | setText(uri.toString()); 24 | } 25 | 26 | #include "moc_GroupChatQrCodeGenerator.cpp" 27 | -------------------------------------------------------------------------------- /src/GroupChatQrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "AbstractQrCodeGenerator.h" 9 | 10 | /** 11 | * Gerenates a QR code encoding the address of a group chat to join it. 12 | */ 13 | class GroupChatQrCodeGenerator : public AbstractQrCodeGenerator 14 | { 15 | Q_OBJECT 16 | 17 | public: 18 | explicit GroupChatQrCodeGenerator(QObject *parent = nullptr); 19 | 20 | private: 21 | void updateText(); 22 | }; 23 | -------------------------------------------------------------------------------- /src/GroupChatUser.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "GroupChatUser.h" 6 | 7 | // Kaidan 8 | #include "RosterModel.h" 9 | 10 | QString GroupChatUser::displayName() const 11 | { 12 | if (const auto rosterItem = RosterModel::instance()->findItem(jid)) { 13 | return rosterItem->name; 14 | } 15 | 16 | if (name.isEmpty()) { 17 | if (jid.isEmpty()) { 18 | return id; 19 | } 20 | 21 | return jid; 22 | } 23 | 24 | return name; 25 | } 26 | 27 | bool GroupChatUser::operator==(const GroupChatUser &other) const 28 | { 29 | // Users can have only an ID (participant in anonymous group chat), only a JID (allowed but not 30 | // joined or banned) or both (participant in normal group chat). 31 | return accountJid == other.accountJid && chatJid == other.chatJid && (id == other.id || jid == other.jid) && name == other.name && status == other.status; 32 | } 33 | 34 | bool GroupChatUser::operator!=(const GroupChatUser &other) const 35 | { 36 | return !operator==(other); 37 | } 38 | 39 | bool GroupChatUser::operator<(const GroupChatUser &other) const 40 | { 41 | if (status == other.status) { 42 | return displayName() < other.displayName(); 43 | } 44 | 45 | return status < other.status; 46 | } 47 | 48 | #include "moc_GroupChatUser.cpp" 49 | -------------------------------------------------------------------------------- /src/GroupChatUser.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | #include 10 | 11 | /** 12 | * This class represents a user of a group chat. 13 | */ 14 | struct GroupChatUser { 15 | Q_GADGET 16 | 17 | public: 18 | enum class Status { 19 | Allowed, 20 | Joined, 21 | Left, 22 | Banned, 23 | }; 24 | Q_ENUM(Status) 25 | // TODO: Use QFlags here to allow combinations such as allowed but not joined 26 | 27 | QString displayName() const; 28 | 29 | bool operator==(const GroupChatUser &other) const; 30 | bool operator!=(const GroupChatUser &other) const; 31 | 32 | bool operator<(const GroupChatUser &other) const; 33 | 34 | QString accountJid; 35 | QString chatJid; 36 | // The ID is part of the primary key in the database. 37 | // Thus, it must not be NULL. 38 | // "" is set because a null string would otherwise be inserted as NULL into the database. 39 | QString id = QStringLiteral(""); 40 | QString jid; 41 | QString name; 42 | Status status; 43 | }; 44 | 45 | Q_DECLARE_METATYPE(GroupChatUser); 46 | -------------------------------------------------------------------------------- /src/GroupChatUserFilterModel.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "GroupChatUserFilterModel.h" 6 | 7 | // Kaidan 8 | #include "GroupChatUserModel.h" 9 | 10 | GroupChatUserFilterModel::GroupChatUserFilterModel(QObject *parent) 11 | : QSortFilterProxyModel(parent) 12 | { 13 | } 14 | 15 | bool GroupChatUserFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const 16 | { 17 | const auto model = static_cast(sourceModel()); 18 | QModelIndex index = model->index(sourceRow, 0, sourceParent); 19 | 20 | return model->data(index, GroupChatUserModel::Role::Name).toString().toLower().contains(filterRegularExpression()) 21 | || model->data(index, GroupChatUserModel::Role::Jid).toString().toLower().contains(filterRegularExpression()); 22 | } 23 | 24 | #include "moc_GroupChatUserFilterModel.cpp" 25 | -------------------------------------------------------------------------------- /src/GroupChatUserFilterModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class GroupChatUserFilterModel : public QSortFilterProxyModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit GroupChatUserFilterModel(QObject *parent = nullptr); 16 | 17 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 18 | }; 19 | -------------------------------------------------------------------------------- /src/GroupChatUserKeyAuthenticationFilterModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class GroupChatUserKeyAuthenticationFilterModel : public QSortFilterProxyModel 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit GroupChatUserKeyAuthenticationFilterModel(QObject *parent = nullptr); 16 | 17 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 18 | 19 | private: 20 | void setUp(); 21 | void handleDevicesChanged(const QString &accountJid, const QList &jids); 22 | void updateJids(); 23 | 24 | QList m_jids; 25 | }; 26 | -------------------------------------------------------------------------------- /src/GuiStyle.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2023 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | #pragma once 8 | 9 | // Qt 10 | #include 11 | #include 12 | 13 | class GuiStyle : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | Q_PROPERTY(QString name READ name CONSTANT) 18 | Q_PROPERTY(bool buttonColoringEnabled READ buttonColoringEnabled CONSTANT) 19 | Q_PROPERTY(bool isMaterial READ isMaterial CONSTANT) 20 | 21 | public: 22 | explicit GuiStyle(QObject *parent = nullptr) 23 | : QObject(parent) 24 | { 25 | } 26 | 27 | inline static QString name() 28 | { 29 | return QQuickStyle::name(); 30 | } 31 | 32 | inline static bool isMaterial() 33 | { 34 | static const bool isMaterial = name().compare(QStringLiteral("Material"), Qt::CaseInsensitive) == 0; 35 | return isMaterial; 36 | } 37 | 38 | // Not all styles actually support coloring buttons. 39 | inline static bool buttonColoringEnabled() 40 | { 41 | return name() == QStringLiteral("Material") || name() == QStringLiteral("org.kde.desktop"); 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /src/HostCompletionModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class RosterModel; 11 | 12 | class HostCompletionModel : public QAbstractListModel 13 | { 14 | Q_OBJECT 15 | 16 | Q_PROPERTY(RosterModel *rosterModel READ rosterModel WRITE setRosterModel NOTIFY rosterModelChanged) 17 | 18 | public: 19 | using QAbstractListModel::QAbstractListModel; 20 | 21 | int rowCount(const QModelIndex &parent = {}) const override; 22 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 23 | 24 | Q_SLOT void clear(); 25 | Q_SLOT void aggregate(const QStringList &jids); 26 | Q_SLOT void aggregateKnownProviders(); 27 | 28 | RosterModel *rosterModel() const; 29 | void setRosterModel(RosterModel *model); 30 | Q_SIGNAL void rosterModelChanged(RosterModel *model); 31 | 32 | private: 33 | QString transform(const QString &entry) const; 34 | QStringList completionProviders() const; 35 | QStringList rosterProviders() const; 36 | void aggregateRoster(); 37 | 38 | private: 39 | QStringList m_hosts; 40 | RosterModel *m_rosterModel = nullptr; 41 | }; 42 | -------------------------------------------------------------------------------- /src/HostCompletionProxyModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class HostCompletionProxyModel : public QSortFilterProxyModel 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString userInput READ userInput WRITE setUserInput NOTIFY userInputChanged) 15 | 16 | public: 17 | explicit HostCompletionProxyModel(QObject *parent = nullptr); 18 | 19 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 20 | 21 | QString userInput() const; 22 | Q_SLOT void setUserInput(const QString &userInput); 23 | Q_SIGNAL void userInputChanged(const QString &userInput); 24 | 25 | protected: 26 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 27 | 28 | private: 29 | QString prefix() const; 30 | QString domain() const; 31 | 32 | private: 33 | QString m_userInput; 34 | }; 35 | -------------------------------------------------------------------------------- /src/LogHandler.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2017 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // QXmpp 11 | #include 12 | 13 | class QXmppClient; 14 | 15 | class LogHandler : public QObject 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | /** 21 | * Default constructor 22 | */ 23 | LogHandler(QXmppClient *client, bool enable, QObject *parent = nullptr); 24 | 25 | /** 26 | * Enable/disable logging to stdout (default: disabled) 27 | */ 28 | void enableLogging(bool enable); 29 | 30 | /** 31 | * Handles logging messages and processes them (currently only output 32 | * of XML streams) 33 | */ 34 | void handleLog(QXmppLogger::MessageType type, const QString &text); 35 | 36 | private: 37 | /** 38 | * Adds new lines to XML data and makes it more readable 39 | */ 40 | static QString makeXmlPretty(QString inputXml); 41 | 42 | QXmppClient *const m_client; 43 | bool enabled = false; 44 | }; 45 | -------------------------------------------------------------------------------- /src/LoginQrCodeGenerator.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "LoginQrCodeGenerator.h" 6 | 7 | // QXmpp 8 | #include 9 | // Kaidan 10 | #include "AccountController.h" 11 | #include "Kaidan.h" 12 | 13 | LoginQrCodeGenerator::LoginQrCodeGenerator(QObject *parent) 14 | : AbstractQrCodeGenerator(parent) 15 | { 16 | connect(this, &LoginQrCodeGenerator::jidChanged, this, &LoginQrCodeGenerator::updateText); 17 | connect(AccountController::instance(), &AccountController::accountChanged, this, &LoginQrCodeGenerator::updateText); 18 | } 19 | 20 | void LoginQrCodeGenerator::updateText() 21 | { 22 | const auto accountController = AccountController::instance(); 23 | QXmpp::Uri::Login loginQuery; 24 | 25 | if (accountController->account().passwordVisibility != Kaidan::PasswordInvisible) { 26 | loginQuery.password = accountController->account().password; 27 | } 28 | 29 | QXmppUri uri; 30 | 31 | uri.setJid(jid()); 32 | uri.setQuery(std::move(loginQuery)); 33 | 34 | setText(uri.toString()); 35 | } 36 | 37 | #include "moc_LoginQrCodeGenerator.cpp" 38 | -------------------------------------------------------------------------------- /src/LoginQrCodeGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Kaidan 8 | #include "AbstractQrCodeGenerator.h" 9 | 10 | /** 11 | * Gerenates a QR code encoding the credentials of an account used to log into it with another 12 | * client. 13 | */ 14 | class LoginQrCodeGenerator : public AbstractQrCodeGenerator 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit LoginQrCodeGenerator(QObject *parent = nullptr); 20 | 21 | private: 22 | void updateText(); 23 | }; 24 | -------------------------------------------------------------------------------- /src/MessageReactionModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2014 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | // Kaidan 10 | #include "MessageModel.h" 11 | 12 | class MessageReactionModel : public QAbstractListModel 13 | { 14 | Q_OBJECT 15 | Q_PROPERTY(QString accountJid MEMBER accountJid) 16 | Q_PROPERTY(QString chatJid MEMBER chatJid) 17 | Q_PROPERTY(QList reactions MEMBER reactions WRITE setReactions) 18 | 19 | public: 20 | enum class Role { 21 | SenderJid = Qt::UserRole + 1, 22 | SenderName, 23 | Emojis, 24 | }; 25 | 26 | explicit MessageReactionModel(QObject *parent = nullptr); 27 | ~MessageReactionModel() override = default; 28 | 29 | [[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override; 30 | [[nodiscard]] QHash roleNames() const override; 31 | [[nodiscard]] QVariant data(const QModelIndex &index, int role) const override; 32 | 33 | void setReactions(const QList &reactions); 34 | 35 | private: 36 | QString accountJid; 37 | QString chatJid; 38 | QList reactions; 39 | }; 40 | -------------------------------------------------------------------------------- /src/PublicGroupChatProxyModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class PublicGroupChatProxyModel : public QSortFilterProxyModel 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString languageFilter READ languageFilter WRITE setLanguageFilter NOTIFY languageFilterChanged) 15 | Q_PROPERTY(int count READ count NOTIFY countChanged) 16 | 17 | public: 18 | explicit PublicGroupChatProxyModel(QObject *parent = nullptr); 19 | 20 | void setSourceModel(QAbstractItemModel *sourceModel) override; 21 | Q_INVOKABLE void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; 22 | 23 | const QString &languageFilter() const; 24 | void setLanguageFilter(const QString &language); 25 | Q_SIGNAL void languageFilterChanged(const QString &language); 26 | 27 | int count() const; 28 | Q_SIGNAL void countChanged(); 29 | 30 | protected: 31 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 32 | 33 | private: 34 | QString m_languageFilter; 35 | }; 36 | -------------------------------------------------------------------------------- /src/RegistrationDataFormFilterModel.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #include "RegistrationDataFormFilterModel.h" 7 | 8 | // Kaidan 9 | #include "RegistrationDataFormModel.h" 10 | 11 | RegistrationDataFormFilterModel::RegistrationDataFormFilterModel(QObject *parent) 12 | : QSortFilterProxyModel(parent) 13 | { 14 | } 15 | 16 | bool RegistrationDataFormFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &) const 17 | { 18 | return !m_filteredRows.contains(sourceRow); 19 | } 20 | 21 | void RegistrationDataFormFilterModel::setSourceModel(QAbstractItemModel *sourceModel) 22 | { 23 | m_filteredRows.clear(); 24 | 25 | auto *dataFormModel = static_cast(sourceModel); 26 | if (dataFormModel) { 27 | m_filteredRows = dataFormModel->indexesToFilter(); 28 | } 29 | 30 | QSortFilterProxyModel::setSourceModel(sourceModel); 31 | Q_EMIT isEmptyChanged(); 32 | } 33 | 34 | bool RegistrationDataFormFilterModel::isEmpty() 35 | { 36 | if (sourceModel()) { 37 | return m_filteredRows.size() == static_cast(sourceModel())->rowCount(); 38 | } 39 | 40 | return true; 41 | } 42 | 43 | #include "moc_RegistrationDataFormFilterModel.cpp" 44 | -------------------------------------------------------------------------------- /src/RegistrationDataFormFilterModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | 11 | /** 12 | * This class is used to filter the data of the registration form. 13 | */ 14 | class RegistrationDataFormFilterModel : public QSortFilterProxyModel 15 | { 16 | Q_OBJECT 17 | Q_PROPERTY(bool isEmpty READ isEmpty NOTIFY isEmptyChanged) 18 | 19 | public: 20 | explicit RegistrationDataFormFilterModel(QObject *parent = nullptr); 21 | 22 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; 23 | void setSourceModel(QAbstractItemModel *sourceModel) override; 24 | 25 | bool isEmpty(); 26 | Q_SIGNAL void isEmptyChanged(); 27 | 28 | private: 29 | QList m_filteredRows; 30 | }; 31 | -------------------------------------------------------------------------------- /src/RegistrationDataFormModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Kaidan 9 | #include "DataFormModel.h" 10 | 11 | /** 12 | * This class is used to store the data of the registration form. 13 | */ 14 | class RegistrationDataFormModel : public DataFormModel 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit RegistrationDataFormModel(QObject *parent = nullptr); 20 | explicit RegistrationDataFormModel(const QXmppDataForm &dataForm, QObject *parent = nullptr); 21 | 22 | Q_INVOKABLE bool hasUsernameField() const; 23 | Q_INVOKABLE bool hasPasswordField() const; 24 | Q_INVOKABLE bool hasEmailField() const; 25 | 26 | Q_INVOKABLE void setUsername(const QString &username); 27 | Q_INVOKABLE void setPassword(const QString &password); 28 | Q_INVOKABLE void setEmail(const QString &email); 29 | 30 | int usernameFieldIndex() const; 31 | int passwordFieldIndex() const; 32 | int emailFieldIndex() const; 33 | 34 | QXmppDataForm::Field extractUsernameField() const; 35 | QXmppDataForm::Field extractPasswordField() const; 36 | QXmppDataForm::Field extractEmailField() const; 37 | 38 | QString extractUsername() const; 39 | QString extractPassword() const; 40 | QString extractEmail() const; 41 | 42 | bool isFakeForm() const; 43 | void setIsFakeForm(bool isFakeForm); 44 | 45 | QList indexesToFilter() const; 46 | 47 | private: 48 | bool m_isFakeForm = false; 49 | }; 50 | -------------------------------------------------------------------------------- /src/RosterItemWatcher.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Linus Jahn 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // std 8 | #include 9 | // Qt 10 | #include 11 | // Kaidan 12 | #include "RosterItem.h" 13 | 14 | class RosterItemWatcher; 15 | 16 | class RosterItemNotifier 17 | { 18 | public: 19 | static RosterItemNotifier &instance(); 20 | 21 | void notifyWatchers(const QString &jid, const std::optional &item); 22 | void registerItemWatcher(const QString &jid, RosterItemWatcher *watcher); 23 | void unregisterItemWatcher(const QString &jid, RosterItemWatcher *watcher); 24 | 25 | private: 26 | RosterItemNotifier() = default; 27 | 28 | std::unordered_multimap m_itemWatchers; 29 | }; 30 | 31 | class RosterItemWatcher : public QObject 32 | { 33 | Q_OBJECT 34 | Q_PROPERTY(QString jid READ jid WRITE setJid NOTIFY jidChanged) 35 | Q_PROPERTY(const RosterItem &item READ item NOTIFY itemChanged) 36 | public: 37 | explicit RosterItemWatcher(QObject *parent = nullptr); 38 | ~RosterItemWatcher(); 39 | 40 | const QString &jid() const; 41 | void setJid(const QString &jid); 42 | Q_SIGNAL void jidChanged(); 43 | 44 | const RosterItem &item() const; 45 | Q_SIGNAL void itemChanged(); 46 | 47 | private: 48 | friend class RosterItemNotifier; 49 | 50 | void notify(const std::optional &item); 51 | void unregister(); 52 | 53 | QString m_jid; 54 | RosterItem m_item; 55 | bool m_outdated = false; 56 | }; 57 | -------------------------------------------------------------------------------- /src/StatusBar.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2016 J-P Nurmi 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | /* 6 | * Copyright (c) 2016 J-P Nurmi 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | */ 26 | 27 | #pragma once 28 | 29 | // Qt 30 | #include 31 | #include 32 | 33 | class StatusBar : public QObject 34 | { 35 | Q_OBJECT 36 | Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) 37 | Q_PROPERTY(bool available READ isAvailable CONSTANT) 38 | 39 | public: 40 | explicit StatusBar(QObject *parent = nullptr); 41 | 42 | QColor color() const; 43 | void setColor(const QColor &color); 44 | Q_SIGNAL void colorChanged(); 45 | 46 | bool isAvailable() const; 47 | 48 | private: 49 | QColor m_color; 50 | }; 51 | -------------------------------------------------------------------------------- /src/SystemUtils.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Linus Jahn 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | namespace SystemUtils 11 | { 12 | 13 | struct LocaleCodes { 14 | QString languageCode; 15 | QString countryCode; 16 | }; 17 | 18 | LocaleCodes systemLocaleCodes(); 19 | 20 | /** 21 | * Returns a pretty product name of the running local system. 22 | * 23 | * This does not contain a version number as QSysInfo::prettyProductName() does. 24 | */ 25 | QString productName(); 26 | 27 | QString audioDirectory(); 28 | QString imageDirectory(); 29 | QString videoDirectory(); 30 | QString downloadDirectory(); 31 | } 32 | -------------------------------------------------------------------------------- /src/TextFormatter.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class QQuickTextDocument; 11 | class QTextCursor; 12 | 13 | class TextFormatter : public QObject 14 | { 15 | Q_OBJECT 16 | 17 | Q_PROPERTY(QQuickTextDocument *textDocument MEMBER m_textDocument WRITE setTextDocument) 18 | Q_PROPERTY(bool enhancedFormatting MEMBER m_enhancedFormatting WRITE setEnhancedFormatting) 19 | 20 | public: 21 | explicit TextFormatter(QObject *parent = nullptr); 22 | 23 | void setTextDocument(QQuickTextDocument *textDocument); 24 | void setEnhancedFormatting(bool enhancedFormatting); 25 | 26 | private: 27 | void update(); 28 | 29 | /** 30 | * Attaches formatting to a text document in order to format the text on each change with 31 | * minimal adjustments. 32 | * 33 | * That results in displaying the text approriately. 34 | * For example, emojis are correctly formatted and a bit enlarged. 35 | */ 36 | void attachNormalFormatting(); 37 | 38 | /** 39 | * Attaches formatting to a text document in order to format the text on each change with all 40 | * available filters. 41 | * 42 | * That results in displaying the text approriately. 43 | * For example, emojis are correctly formatted and enlarged depending on their count while links 44 | * are marked as such and appropriately highlighted. 45 | */ 46 | void attachEnhancedFormatting(); 47 | 48 | void attachFormatting(const std::function &formatText, int start = 0, int addedCharactersCount = 0); 49 | 50 | QQuickTextDocument *m_textDocument = nullptr; 51 | bool m_enhancedFormatting = false; 52 | }; 53 | -------------------------------------------------------------------------------- /src/TrustMessageUriGenerator.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class TrustMessageUriGenerator : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | Q_PROPERTY(QString jid MEMBER m_jid WRITE setJid) 15 | Q_PROPERTY(QString uri READ uri NOTIFY uriChanged) 16 | 17 | public: 18 | explicit TrustMessageUriGenerator(QObject *parent = nullptr); 19 | 20 | void setJid(const QString &jid); 21 | 22 | QString uri() const; 23 | Q_SIGNAL void uriChanged(); 24 | 25 | protected: 26 | QString jid() const; 27 | Q_SIGNAL void jidChanged(); 28 | 29 | void setKeys(const QList &authenticatedKeys, const QList &distrustedKeys); 30 | 31 | private: 32 | void setUp(); 33 | void handleKeysChanged(const QString &accountJid, const QList &jids); 34 | 35 | QString m_jid; 36 | 37 | QList m_authenticatedKeys; 38 | QList m_distrustedKeys; 39 | }; 40 | -------------------------------------------------------------------------------- /src/UserDevicesModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // Kaidan 11 | #include "PresenceCache.h" 12 | 13 | class QXmppVersionIq; 14 | 15 | class UserDevicesModel : public QAbstractListModel 16 | { 17 | Q_OBJECT 18 | 19 | Q_PROPERTY(QString jid READ jid WRITE setJid NOTIFY jidChanged) 20 | 21 | public: 22 | enum Roles { 23 | Resource = Qt::UserRole + 1, 24 | Name, 25 | Version, 26 | OS, 27 | }; 28 | 29 | explicit UserDevicesModel(QObject *parent = nullptr); 30 | 31 | QHash roleNames() const override; 32 | QVariant data(const QModelIndex &index, int role) const override; 33 | int rowCount(const QModelIndex &parent) const override; 34 | 35 | QString jid() const; 36 | void setJid(const QString &jid); 37 | 38 | Q_SIGNALS: 39 | void jidChanged(); 40 | void clientVersionsRequested(const QString &bareJid, const QString &resource = {}); 41 | 42 | private: 43 | void handleClientVersionReceived(const QXmppVersionIq &versionIq); 44 | void handlePresenceChanged(PresenceCache::ChangeType type, const QString &jid, const QString &resource); 45 | void handlePresencesCleared(); 46 | 47 | struct DeviceInfo { 48 | explicit DeviceInfo(const QString &resource); 49 | explicit DeviceInfo(const QXmppVersionIq &); 50 | 51 | QString resource; 52 | QString name; 53 | QString version; 54 | QString os; 55 | }; 56 | 57 | QString m_jid; 58 | QList m_devices; 59 | }; 60 | -------------------------------------------------------------------------------- /src/VCardCache.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Linus Jahn 2 | // SPDX-FileCopyrightText: 2021 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #include "VCardCache.h" 7 | 8 | // Qt 9 | #include 10 | 11 | VCardCache::VCardCache(QObject *parent) 12 | : QObject(parent) 13 | { 14 | } 15 | 16 | std::optional VCardCache::vCard(const QString &jid) const 17 | { 18 | QMutexLocker locker(&m_mutex); 19 | if (m_vCards.contains(jid)) 20 | return m_vCards.value(jid); 21 | return std::nullopt; 22 | } 23 | 24 | void VCardCache::setVCard(const QString &jid, const QXmppVCardIq &vCard) 25 | { 26 | QMutexLocker locker(&m_mutex); 27 | if (m_vCards.value(jid) != vCard) { 28 | m_vCards.insert(jid, vCard); 29 | locker.unlock(); 30 | Q_EMIT vCardChanged(jid); 31 | } 32 | } 33 | 34 | #include "moc_VCardCache.cpp" 35 | -------------------------------------------------------------------------------- /src/VCardCache.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Linus Jahn 2 | // SPDX-FileCopyrightText: 2021 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // std 9 | #include 10 | // Qt 11 | #include 12 | #include 13 | #include 14 | // QXmpp 15 | #include 16 | 17 | class VCardCache : public QObject 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit VCardCache(QObject *parent = nullptr); 23 | 24 | /** 25 | * Returns the vCard for a JID. 26 | * 27 | * This method is thread-safe. 28 | * 29 | * @param jid JID for which the vCard is retrieved 30 | */ 31 | std::optional vCard(const QString &jid) const; 32 | 33 | /** 34 | * Sets the vCard for a JID. 35 | * 36 | * This method is thread-safe. 37 | * 38 | * @param jid JID to which the vCard belongs 39 | * @param vCard vCard being set 40 | */ 41 | void setVCard(const QString &jid, const QXmppVCardIq &vCard); 42 | 43 | Q_SIGNALS: 44 | /** 45 | * Emitted when a vCard changed. 46 | * 47 | * @param jid JID of the changed vCard 48 | */ 49 | void vCardChanged(const QString &jid); 50 | 51 | private: 52 | mutable QMutex m_mutex; 53 | 54 | // mapping from a JID to the vCard of that JID 55 | QHash m_vCards; 56 | }; 57 | -------------------------------------------------------------------------------- /src/VCardModel.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2019 Linus Jahn 2 | // SPDX-FileCopyrightText: 2019 Robert Maerkisch 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | // QXmpp 11 | #include 12 | 13 | class QXmppVCardIq; 14 | 15 | class VCardModel : public QAbstractListModel 16 | { 17 | Q_OBJECT 18 | Q_PROPERTY(QString jid READ jid WRITE setJid NOTIFY jidChanged) 19 | Q_PROPERTY(bool unsetEntriesProcessed MEMBER m_unsetEntriesProcessed NOTIFY unsetEntriesProcessedChanged) 20 | 21 | public: 22 | enum Roles { 23 | Key, 24 | Value, 25 | UriScheme, 26 | }; 27 | 28 | struct Item { 29 | QString name; 30 | std::function value; 31 | std::function setValue; 32 | QString uriScheme = {}; 33 | }; 34 | 35 | explicit VCardModel(QObject *parent = nullptr); 36 | 37 | QHash roleNames() const override; 38 | int rowCount(const QModelIndex &parent = QModelIndex()) const override; 39 | 40 | QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; 41 | bool setData(const QModelIndex &index, const QVariant &value, int role) override; 42 | 43 | QString jid() const; 44 | void setJid(const QString &jid); 45 | 46 | void generateEntries(); 47 | 48 | Q_SIGNALS: 49 | void jidChanged(); 50 | void unsetEntriesProcessedChanged(); 51 | 52 | private: 53 | void handleVCardReceived(const QXmppVCardIq &vCard); 54 | 55 | QString m_jid; 56 | bool m_unsetEntriesProcessed = false; 57 | QXmppVCardIq m_vCard; 58 | QList m_vCardMap; 59 | }; 60 | -------------------------------------------------------------------------------- /src/VersionController.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #include "VersionController.h" 7 | 8 | // QXmpp 9 | #include 10 | #include 11 | // Kaidan 12 | #include "FutureUtils.h" 13 | #include "Globals.h" 14 | #include "Kaidan.h" 15 | 16 | VersionController::VersionController(QObject *parent) 17 | : QObject(parent) 18 | , m_rosterManager(Kaidan::instance()->client()->rosterManager()) 19 | , m_versionManager(Kaidan::instance()->client()->versionManager()) 20 | { 21 | // Publish the own operating system and client information. 22 | m_versionManager->setClientName(QStringLiteral(APPLICATION_DISPLAY_NAME)); 23 | m_versionManager->setClientVersion(QStringLiteral(VERSION_STRING)); 24 | m_versionManager->setClientOs(QSysInfo::prettyProductName()); 25 | 26 | connect(m_versionManager, &QXmppVersionManager::versionReceived, this, &VersionController::clientVersionReceived); 27 | } 28 | 29 | void VersionController::fetchVersions(const QString &bareJid, const QString &resource) 30 | { 31 | const auto fetchVersion = [this, &bareJid](const QString &res) { 32 | m_versionManager->requestVersion(bareJid % u'/' % res); 33 | }; 34 | 35 | if (resource.isEmpty()) { 36 | runOnThread(m_versionManager, [this, bareJid, resource, fetchVersion]() { 37 | const auto resources = m_rosterManager->getResources(bareJid); 38 | std::for_each(resources.cbegin(), resources.cend(), fetchVersion); 39 | }); 40 | } else { 41 | runOnThread(m_versionManager, [resource, fetchVersion]() { 42 | fetchVersion(resource); 43 | }); 44 | } 45 | } 46 | 47 | #include "moc_VersionController.cpp" 48 | -------------------------------------------------------------------------------- /src/VersionController.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | #pragma once 7 | 8 | // Qt 9 | #include 10 | 11 | class QXmppClient; 12 | class QXmppRosterManager; 13 | class QXmppVersionIq; 14 | class QXmppVersionManager; 15 | 16 | class VersionController : public QObject 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit VersionController(QObject *parent = nullptr); 22 | 23 | /** 24 | * Fetches the version information of all resources of the given bare JID 25 | */ 26 | Q_INVOKABLE void fetchVersions(const QString &bareJid, const QString &resource); 27 | 28 | Q_SIGNALS: 29 | /** 30 | * Emitted when a client version information was received 31 | */ 32 | void clientVersionReceived(const QXmppVersionIq &versionIq); 33 | 34 | private: 35 | QXmppRosterManager *const m_rosterManager; 36 | QXmppVersionManager *const m_versionManager; 37 | }; 38 | -------------------------------------------------------------------------------- /src/app/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2025 Laurent Montel 2 | # 3 | # SPDX-License-Identifier: CC0-1.0 4 | 5 | add_executable(${PROJECT_NAME} WIN32) 6 | target_sources(${PROJECT_NAME} PRIVATE main.cpp) 7 | target_link_libraries(${PROJECT_NAME} Kaidan::Core) 8 | ecm_create_qm_loader(${PROJECT_NAME} kaidan_qt) 9 | 10 | ecm_qt_declare_logging_category( 11 | ${PROJECT_NAME} 12 | HEADER KaidanLog.h 13 | IDENTIFIER KAIDAN_LOG 14 | CATEGORY_NAME im.kaidan.kaidan 15 | DESCRIPTION "Kaidan" 16 | EXPORT KAIDAN 17 | ) 18 | 19 | if (TARGET KF6::Crash) 20 | target_link_libraries(${PROJECT_NAME} KF6::Crash) 21 | endif() 22 | 23 | if (NOT ANDROID) 24 | target_link_libraries(${PROJECT_NAME} KDAB::kdsingleapplication KF6::WindowSystem) 25 | endif() 26 | if (NOT WIN32 AND NOT APPLE) 27 | target_link_libraries(${PROJECT_NAME} Qt::GuiPrivate) 28 | endif() 29 | 30 | 31 | # Set a custom plist file for the app bundle 32 | if(APPLE) 33 | if(IOS) 34 | set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/misc/ios/Info.plist) 35 | else() 36 | set_target_properties(${PROJECT_NAME} PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/misc/macos/Info.plist) 37 | endif() 38 | endif() 39 | 40 | -------------------------------------------------------------------------------- /src/qml/AboutDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | 7 | import im.kaidan.kaidan 8 | 9 | import "elements" 10 | 11 | FormInfoDialog { 12 | title: qsTr("About Kaidan") 13 | 14 | AboutHeader {} 15 | AboutContent { 16 | Layout.fillWidth: true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/qml/AboutPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | 7 | import im.kaidan.kaidan 8 | 9 | import "elements" 10 | 11 | FormInfoPage { 12 | title: qsTr("About Kaidan") 13 | 14 | AboutHeader {} 15 | AboutContent { 16 | Layout.fillWidth: true 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/qml/ChatPageBase.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2019 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import org.kde.kirigami as Kirigami 6 | 7 | /** 8 | * This is the base for a chat page. 9 | */ 10 | ImageBackgroundPage { 11 | bottomPadding: Kirigami.Units.largeSpacing 12 | } 13 | -------------------------------------------------------------------------------- /src/qml/EmptyChatPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2019 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2021 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | import QtQuick.Layouts 9 | import org.kde.kirigami as Kirigami 10 | 11 | import "elements" 12 | 13 | ChatPageBase { 14 | RowLayout { 15 | spacing: 0 16 | anchors.fill: parent 17 | 18 | Item { 19 | Layout.fillWidth: true 20 | } 21 | 22 | ChatInfo { 23 | text: qsTr("Select a chat to start") 24 | level: 1 25 | type: Kirigami.Heading.Type.Primary 26 | wrapMode: Text.Wrap 27 | horizontalAlignment: Text.AlignHCenter 28 | Layout.maximumWidth: parent.width 29 | } 30 | 31 | Item { 32 | Layout.fillWidth: true 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/qml/ImageBackgroundPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | /** 11 | * This is a scrollable page with an image as its background. 12 | */ 13 | Kirigami.ScrollablePage { 14 | background: Rectangle { 15 | color: secondaryBackgroundColor 16 | 17 | Image { 18 | source: Utils.getResourcePath("images/chat-page-background.svg") 19 | anchors.fill: parent 20 | fillMode: Image.Tile 21 | horizontalAlignment: Image.AlignLeft 22 | verticalAlignment: Image.AlignTop 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/qml/details/AccountDetailsDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | 9 | import im.kaidan.kaidan 10 | 11 | import "../elements" 12 | 13 | FormInfoDialog { 14 | id: root 15 | 16 | property string jid 17 | 18 | title: qsTr("Account Details") 19 | 20 | AccountDetailsHeader { 21 | dialog: root 22 | jid: root.jid 23 | } 24 | 25 | AccountDetailsContent { 26 | dialog: root 27 | jid: root.jid 28 | Layout.fillWidth: true 29 | } 30 | 31 | Connections { 32 | target: Kaidan 33 | 34 | // Close this dialog when the account is removed. 35 | function onCredentialsNeeded() { 36 | root.close() 37 | } 38 | 39 | // Close this dialog when the chat with oneself is added via it. 40 | function onOpenChatPageRequested(accountJid, chatJid) { 41 | root.close() 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/qml/details/AccountDetailsHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | DetailsHeader { 11 | id: root 12 | 13 | property Kirigami.Dialog dialog 14 | 15 | displayName: AccountController.account.displayName 16 | avatarAction: Kirigami.Action { 17 | text: qsTr("Change your profile image") 18 | icon.name: "camera-photo-symbolic" 19 | onTriggered: { 20 | if (root.dialog) { 21 | root.dialog.close() 22 | } 23 | 24 | openPage(avatarChangePage) 25 | } 26 | } 27 | 28 | function changeDisplayName(newDisplayName) { 29 | Kaidan.vCardController.changeNickname(newDisplayName) 30 | } 31 | 32 | function handleDisplayNameChanged() { 33 | if (Kaidan.connectionState === Enums.StateConnected) { 34 | Kaidan.vCardController.requestClientVCard() 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/qml/details/AccountDetailsPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | 8 | import im.kaidan.kaidan 9 | 10 | import "../elements" 11 | 12 | FormInfoPage { 13 | id: root 14 | 15 | property string jid 16 | 17 | title: qsTr("Account Details") 18 | 19 | AccountDetailsHeader { 20 | jid: root.jid 21 | } 22 | 23 | AccountDetailsContent { 24 | jid: root.jid 25 | Layout.fillWidth: true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/qml/details/ContactDetailsDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2017 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 4 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 5 | // 6 | // SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | import QtQuick 9 | import QtQuick.Layouts 10 | 11 | import im.kaidan.kaidan 12 | 13 | import "../elements" 14 | 15 | FormInfoDialog { 16 | id: root 17 | title: qsTr("Contact Details") 18 | 19 | ContactDetailsHeader {} 20 | 21 | ContactDetailsContent { 22 | dialog: root 23 | Layout.fillWidth: true 24 | } 25 | 26 | Connections { 27 | target: Kaidan 28 | 29 | // Close this dialog when the contact is removed. 30 | function onCloseChatPageRequested() { 31 | root.close() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/qml/details/ContactDetailsHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | import "../elements" 13 | 14 | RosterItemDetailsHeader { 15 | id: root 16 | avatarAction: Kirigami.Action { 17 | text: qsTr("Maximize avatar") 18 | icon.name: "view-fullscreen-symbolic" 19 | enabled: Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid).toString() 20 | onTriggered: Qt.openUrlExternally(Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid)) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/qml/details/ContactDetailsPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | 9 | import "../elements" 10 | 11 | FormInfoPage { 12 | id: root 13 | title: qsTr("Contact Details") 14 | 15 | ContactDetailsHeader {} 16 | 17 | ContactDetailsContent { 18 | Layout.fillWidth: true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/qml/details/GroupChatDetailsDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2017 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 4 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 5 | // 6 | // SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | import QtQuick 9 | import QtQuick.Layouts 10 | 11 | import im.kaidan.kaidan 12 | 13 | import "../elements" 14 | 15 | FormInfoDialog { 16 | id: root 17 | title: qsTr("Group Details") 18 | 19 | GroupChatDetailsHeader {} 20 | 21 | GroupChatDetailsContent { 22 | id: content 23 | dialog: root 24 | Layout.fillWidth: true 25 | } 26 | 27 | Connections { 28 | target: Kaidan 29 | 30 | // Close this dialog when the group chat is removed. 31 | function onCloseChatPageRequested() { 32 | root.close() 33 | } 34 | } 35 | 36 | function openContactListView() { 37 | content.openContactListView() 38 | } 39 | 40 | function openKeyAuthenticationUserListView() { 41 | content.openKeyAuthenticationUserListView() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/qml/details/GroupChatDetailsHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | import "../elements" 13 | 14 | RosterItemDetailsHeader { 15 | id: root 16 | isGroupChat: true 17 | avatarAction: Kirigami.Action { 18 | text: qsTr("Maximize avatar") 19 | icon.name: "view-fullscreen-symbolic" 20 | enabled: Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid).toString() 21 | onTriggered: Qt.openUrlExternally(Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid)) 22 | } 23 | description.text: ChatController.rosterItem.isPublicGroupChat ? qsTr("Public Group Chat") : qsTr("Private Group Chat") 24 | 25 | Controls.Label { 26 | text: qsTr("Deleted") 27 | color: Kirigami.Theme.negativeTextColor 28 | visible: ChatController.rosterItem.isDeletedGroupChat 29 | wrapMode: Text.Wrap 30 | Layout.fillWidth: true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/qml/details/GroupChatDetailsPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | 9 | import "../elements" 10 | 11 | FormInfoPage { 12 | id: root 13 | title: qsTr("Group Details") 14 | 15 | GroupChatDetailsHeader {} 16 | 17 | GroupChatDetailsContent { 18 | id: content 19 | Layout.fillWidth: true 20 | } 21 | 22 | function openContactListView() { 23 | content.openContactListView() 24 | } 25 | 26 | function openKeyAuthenticationUserListView() { 27 | content.openKeyAuthenticationUserListView() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/qml/details/KeyAuthenticationButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import org.kde.kirigamiaddons.formcard as FormCard 6 | 7 | import im.kaidan.kaidan 8 | 9 | FormCard.FormButtonDelegate { 10 | property EncryptionWatcher encryptionWatcher 11 | property int authenticationState 12 | } 13 | -------------------------------------------------------------------------------- /src/qml/details/NotesChatDetailsDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | 9 | import im.kaidan.kaidan 10 | 11 | import "../elements" 12 | 13 | FormInfoDialog { 14 | id: root 15 | title: qsTr("Notes Details") 16 | 17 | NotesChatDetailsHeader {} 18 | 19 | NotesChatDetailsContent { 20 | dialog: root 21 | Layout.fillWidth: true 22 | } 23 | 24 | Connections { 25 | target: Kaidan 26 | 27 | // Close this dialog when the notes chat is removed. 28 | function onCloseChatPageRequested() { 29 | root.close() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/qml/details/NotesChatDetailsHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | import "../elements" 13 | 14 | ContactDetailsHeader { 15 | id: root 16 | description { 17 | text: qsTr("Messages in this chat are synchronized as notes across all your devices") 18 | visible: ChatController.accountJid === root.jid 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/qml/details/NotesChatDetailsPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | 8 | import im.kaidan.kaidan 9 | 10 | import "../elements" 11 | 12 | FormInfoPage { 13 | id: root 14 | title: qsTr("Notes Details") 15 | 16 | NotesChatDetailsHeader {} 17 | 18 | NotesChatDetailsContent { 19 | Layout.fillWidth: true 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/details/RosterItemDetailsHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | import "../elements" 13 | 14 | DetailsHeader { 15 | id: root 16 | jid: ChatController.chatJid 17 | 18 | property alias description: description 19 | 20 | displayName: ChatController.rosterItem.displayName 21 | avatarAction: Kirigami.Action { 22 | text: qsTr("Maximize avatar") 23 | icon.name: "view-fullscreen-symbolic" 24 | enabled: Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid).toString() 25 | onTriggered: Qt.openUrlExternally(Kaidan.avatarStorage.getAvatarUrl(ChatController.chatJid)) 26 | } 27 | 28 | Controls.Label { 29 | id: description 30 | color: Kirigami.Theme.neutralTextColor 31 | wrapMode: Text.Wrap 32 | Layout.fillWidth: true 33 | } 34 | 35 | function changeDisplayName(newDisplayName) { 36 | Kaidan.rosterController.renameContact(ChatController.chatJid, newDisplayName) 37 | } 38 | 39 | function handleDisplayNameChanged() {} 40 | } 41 | -------------------------------------------------------------------------------- /src/qml/details/UserDevicesArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Mathis Brüchert 2 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | import QtQuick.Controls as Controls 9 | import QtQuick.Layouts 10 | import org.kde.kirigami as Kirigami 11 | import org.kde.kirigamiaddons.formcard as FormCard 12 | 13 | import im.kaidan.kaidan 14 | 15 | import "../elements" 16 | 17 | FormCard.FormCard { 18 | id: root 19 | 20 | required property string jid 21 | 22 | visible: deviceRepeater.count 23 | Layout.fillWidth: true 24 | 25 | FormCard.FormHeader { 26 | title: qsTr("Connected Devices") 27 | } 28 | 29 | Repeater { 30 | id: deviceRepeater 31 | Layout.fillHeight: true 32 | model: UserDevicesModel { 33 | jid: root.jid 34 | } 35 | delegate: FormCard.AbstractFormDelegate { 36 | visible: deviceExpansionButton.checked 37 | background: null 38 | contentItem: ColumnLayout { 39 | Controls.Label { 40 | text: { 41 | if (model.name) { 42 | if (model.version) { 43 | return model.name + " " + model.version 44 | } 45 | return model.name 46 | } 47 | return model.resource 48 | } 49 | textFormat: Text.PlainText 50 | wrapMode: Text.WordWrap 51 | Layout.fillWidth: true 52 | } 53 | 54 | Controls.Label { 55 | text: model.os 56 | color: Kirigami.Theme.disabledTextColor 57 | font: Kirigami.Theme.smallFont 58 | textFormat: Text.PlainText 59 | wrapMode: Text.WordWrap 60 | Layout.fillWidth: true 61 | } 62 | } 63 | } 64 | } 65 | 66 | FormExpansionButton { 67 | id: deviceExpansionButton 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/qml/details/UserKeyAuthenticationButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | KeyAuthenticationButton { 6 | authenticationState: { 7 | if (!encryptionWatcher.hasUsableDevices) { 8 | if (encryptionWatcher.hasDistrustedDevices) { 9 | return DetailsContent.EncryptionKeyAuthenticationState.DistrustedKeysOnly 10 | } 11 | 12 | return DetailsContent.EncryptionKeyAuthenticationState.NoKeys 13 | } else if (encryptionWatcher.hasAuthenticatableDevices) { 14 | if (encryptionWatcher.hasAuthenticatableDistrustedDevices) { 15 | return DetailsContent.EncryptionKeyAuthenticationState.AuthenticatableDistrustedKeys 16 | } 17 | 18 | return DetailsContent.EncryptionKeyAuthenticationState.AuthenticatableTrustedKeysOnly 19 | } 20 | 21 | return DetailsContent.EncryptionKeyAuthenticationState.AllKeysAuthenticated 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/qml/elements/AboutHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | FormInfoHeader { 13 | id: root 14 | 15 | Image { 16 | source: Utils.getResourcePath("images/kaidan.svg") 17 | Layout.alignment: Qt.AlignHCenter 18 | Layout.margins: - Kirigami.Units.largeSpacing 19 | Layout.preferredHeight: Kirigami.Units.gridUnit * 8 20 | Layout.preferredWidth: Layout.preferredHeight 21 | fillMode: Image.PreserveAspectFit 22 | mipmap: true 23 | sourceSize: Qt.size(width, height) 24 | } 25 | 26 | ColumnLayout { 27 | spacing: Kirigami.Units.largeSpacing 28 | Layout.leftMargin: root.flow === GridLayout.LeftToRight ? Kirigami.Units.largeSpacing * 2 : 0 29 | 30 | Kirigami.Heading { 31 | id: applicationNameHeading 32 | text: Utils.applicationDisplayName + " " + Utils.versionString 33 | textFormat: Text.PlainText 34 | wrapMode: Text.WordWrap 35 | Layout.fillWidth: true 36 | horizontalAlignment: Qt.AlignLeft 37 | } 38 | 39 | Controls.Label { 40 | text: qsTr("Modern chat app for every device") 41 | font.italic: true 42 | wrapMode: Text.WordWrap 43 | Layout.fillWidth: true 44 | horizontalAlignment: Qt.AlignLeft 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/qml/elements/AccountKeyAuthenticationPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Mathis Brüchert 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2023 Linus Jahn 4 | // SPDX-FileCopyrightText: 2023 Bhavy Airi 5 | // 6 | // SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | import QtQuick 9 | import QtQuick.Layouts 10 | import QtQuick.Controls as Controls 11 | import org.kde.kirigami as Kirigami 12 | import org.kde.kirigamiaddons.formcard as FormCard 13 | 14 | import im.kaidan.kaidan 15 | 16 | /** 17 | * This page is used for authenticating encryption keys of the own account by scanning QR codes or entering key IDs. 18 | */ 19 | KeyAuthenticationPage { 20 | id: root 21 | jid: accountJid 22 | explanation: ExplanationArea { 23 | primaryExplanationText.text: qsTr("Step 1: Scan your other device's QR code") 24 | primaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-own-1.svg") 25 | secondaryExplanationText.text: qsTr("Step 2: Scan with your other device this device's QR code") 26 | secondaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-own-2.svg") 27 | } 28 | authenticatableKeysArea.header.title: qsTr("Unverified own devices") 29 | authenticatableKeysArea.listView.model: AuthenticatableEncryptionKeyModel { 30 | accountJid: root.accountJid 31 | chatJid: accountJid 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/qml/elements/AccountQrCode.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import im.kaidan.kaidan 6 | 7 | QrCode { 8 | id: root 9 | source: qrCodeGenerator.qrCode 10 | 11 | AccountQrCodeGenerator { 12 | id: qrCodeGenerator 13 | jid: root.jid 14 | edgePixelCount: root.width 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/qml/elements/Avatar.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2019 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2021 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2021 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | import org.kde.kirigamiaddons.components as Components 9 | 10 | import im.kaidan.kaidan 11 | 12 | Components.Avatar { 13 | property string jid 14 | property bool isGroupChat: false 15 | 16 | source: jid ? Kaidan.avatarStorage.getAvatarUrl(jid) : "" 17 | iconSource: isGroupChat ? "system-users-symbolic" : "avatar-default-symbolic" 18 | color: Utils.userColor(jid, name) 19 | } 20 | -------------------------------------------------------------------------------- /src/qml/elements/BusyIndicatorFormButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | import org.kde.kirigamiaddons.formcard as FormCard 10 | 11 | /** 12 | * This button contains a busy indicator for an action without an instantaneous result. 13 | * 14 | * It is intended to be used within the contentItem of a FormCard.FormCard. 15 | */ 16 | FormCard.FormButtonDelegate { 17 | property string idleText 18 | property string busyText 19 | property bool busy: false 20 | property string idleIconSource: "emblem-ok-symbolic" 21 | 22 | text: busy ? busyText : idleText 23 | enabled: !busy 24 | font.italic: busy 25 | // Needed to position "leading" appropriately. 26 | // It would otherwise have a too small right margin. 27 | leadingPadding: Kirigami.Units.smallSpacing * 3 28 | // Using `"icon.name: busy ? "" : "emblem-ok-symbolic"` and setting busyIndicator as leading 29 | // results in a strange larger leftPadding for the icon if busyIndicator is not loaded. 30 | // Thus, the icon is set as leading as well. 31 | leading: Loader { 32 | sourceComponent: busy ? busyIndicator : idleIcon 33 | 34 | Component { 35 | id: idleIcon 36 | 37 | Kirigami.Icon { 38 | source: idleIconSource 39 | implicitWidth: Kirigami.Units.iconSizes.small 40 | implicitHeight: Kirigami.Units.iconSizes.small 41 | } 42 | } 43 | 44 | Component { 45 | id: busyIndicator 46 | 47 | Controls.BusyIndicator { 48 | implicitWidth: Kirigami.Units.iconSizes.small 49 | implicitHeight: implicitWidth 50 | // Needed to make the item larger without increasing the parent's height. 51 | padding: - 2 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/qml/elements/Button.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | /** 13 | * This is a button fitting mobile and desktop user interfaces. 14 | */ 15 | Controls.Button { 16 | property bool remainTooltip: false 17 | 18 | flat: Style.isMaterial 19 | hoverEnabled: true 20 | Controls.ToolTip.delay: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.veryLongDuration * 2 21 | Controls.ToolTip.timeout: Kirigami.Units.veryLongDuration * 10 22 | onHoveredChanged: { 23 | if (Controls.ToolTip.text && !Kirigami.Settings.isMobile) { 24 | if (hovered) { 25 | Controls.ToolTip.show(Controls.ToolTip.text, Controls.ToolTip.timeout) 26 | } else { 27 | Controls.ToolTip.hide() 28 | } 29 | } 30 | } 31 | onPressed: { 32 | if (Controls.ToolTip.text && !Kirigami.Settings.isMobile) { 33 | Controls.ToolTip.hide() 34 | } 35 | } 36 | onPressAndHold: { 37 | if (Controls.ToolTip.text && Kirigami.Settings.isMobile) { 38 | remainTooltip = true 39 | Controls.ToolTip.show(Controls.ToolTip.text, Controls.ToolTip.timeout) 40 | } 41 | } 42 | onReleased: { 43 | if (Controls.ToolTip.text && Kirigami.Settings.isMobile) { 44 | if (remainTooltip) { 45 | remainTooltip = false 46 | Controls.ToolTip.show(Controls.ToolTip.text, Controls.ToolTip.timeout) 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/qml/elements/CameraStatus.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2021 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | import QtQuick.Layouts 9 | import QtQuick.Controls as Controls 10 | import QtMultimedia 11 | import org.kde.kirigami as Kirigami 12 | 13 | /** 14 | * This is a hint for the status of a camera. 15 | */ 16 | Rectangle { 17 | id: root 18 | 19 | color: primaryBackgroundColor 20 | 21 | ColumnLayout { 22 | anchors.fill: parent 23 | anchors.margins: Kirigami.Units.largeSpacing 24 | 25 | // The layout is needed to position the icon and text in the center without additional 26 | // spacing between them while keeping the text's end being elided. 27 | ColumnLayout { 28 | Kirigami.Icon { 29 | id: cameraStatusIcon 30 | source: "camera-disabled-symbolic" 31 | fallback: "camera-off-symbolic" 32 | Layout.maximumWidth: Kirigami.Units.iconSizes.enormous 33 | Layout.maximumHeight: Layout.maximumWidth 34 | Layout.fillWidth: true 35 | Layout.fillHeight: true 36 | Layout.alignment: Qt.AlignHCenter 37 | } 38 | 39 | Kirigami.Heading { 40 | text: qsTr("No camera available") 41 | wrapMode: Text.Wrap 42 | elide: Text.ElideRight 43 | horizontalAlignment: Text.AlignHCenter 44 | Layout.fillWidth: true 45 | // "Layout.fillHeight: true" cannot be used to position the icon and text in the 46 | // center without additional spacing between them while keeping the text's end 47 | // being elided. 48 | Layout.fillHeight: cameraStatusIcon.height + parent.spacing + implicitHeight > parent.parent.height 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/qml/elements/CenteredAdaptiveButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This is a centered button having an adjustable label and fitting its parent's width. 15 | */ 16 | Button { 17 | Layout.alignment: Qt.AlignHCenter 18 | Layout.fillWidth: true 19 | 20 | Kirigami.Theme.textColor: { 21 | if (Style.isMaterial) 22 | return Kirigami.Theme.positiveTextColor 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/qml/elements/CenteredAdaptiveHighlightedButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2023 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * This is a highlighted button. 13 | * 14 | * It is used for main actions. 15 | */ 16 | CenteredAdaptiveButton { 17 | Kirigami.Theme.textColor: Style.buttonColoringEnabled ? Kirigami.Theme.highlightedTextColor : Kirigami.Theme.textColor 18 | Kirigami.Theme.backgroundColor: positive ? Kirigami.Theme.positiveTextColor : Kirigami.Theme.negativeTextColor 19 | 20 | // Set the property to 'false' for a cancellation or a dangerous action. 21 | property bool positive: true 22 | } 23 | -------------------------------------------------------------------------------- /src/qml/elements/CenteredAdaptiveText.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | /** 12 | * This is a centered and adaptive text. 13 | */ 14 | ScalableText { 15 | horizontalAlignment: Text.AlignHCenter 16 | wrapMode: Text.Wrap 17 | Layout.fillWidth: true 18 | } 19 | -------------------------------------------------------------------------------- /src/qml/elements/ChatInfo.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | Kirigami.Heading { 10 | level: 5 11 | leftPadding: font.pixelSize * 0.7 12 | rightPadding: leftPadding 13 | topPadding: leftPadding * 0.4 14 | bottomPadding: topPadding 15 | background: Kirigami.ShadowedRectangle { 16 | color: primaryBackgroundColor 17 | radius: parent.height * 0.5 18 | shadow.color: Qt.darker(color, 1.2) 19 | shadow.size: 4 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/elements/ChatMessageContextMenuButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | /** 11 | * This is a button used within a ChatMessageContextMenu. 12 | */ 13 | ClickableIcon { 14 | property Kirigami.Dialog contextMenu 15 | property bool expanded: false 16 | property bool shown: true 17 | property alias expansionTimer: expansionTimer 18 | 19 | visible: expanded && shown 20 | onClicked: contextMenu.close() 21 | 22 | Timer { 23 | id: expansionTimer 24 | onTriggered: parent.expanded = true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/qml/elements/ClickableIcon.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | /** 11 | * This is a clickable icon changing its color on several events if possible. 12 | */ 13 | Kirigami.Icon { 14 | // Icons which cannot be colored (e.g., emojis) are highlighted in another way. 15 | active: mouseArea.containsMouse 16 | implicitWidth: Kirigami.Units.iconSizes.smallMedium 17 | implicitHeight: Kirigami.Units.iconSizes.smallMedium 18 | 19 | property alias mouseArea: mouseArea 20 | signal clicked() 21 | 22 | ClickableMouseArea { 23 | id: mouseArea 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/qml/elements/ClickableMouseArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | /** 11 | * This is a mouse area with tooltip functionality and color changes on several events if possible. 12 | * 13 | * The parent of this item needs to have a "color" property and a "clicked" signal. 14 | */ 15 | MouseArea { 16 | property bool remainTooltip: false 17 | 18 | anchors.fill: parent 19 | hoverEnabled: true 20 | Controls.ToolTip.text: parent.Controls.ToolTip.text 21 | Controls.ToolTip.delay: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.veryLongDuration * 2 22 | Controls.ToolTip.timeout: Kirigami.Units.veryLongDuration * 10 23 | onEntered: { 24 | parent.color = Kirigami.Theme.hoverColor 25 | 26 | if (Controls.ToolTip.text && !Kirigami.Settings.isMobile) { 27 | Controls.ToolTip.visible = true 28 | } 29 | } 30 | onPressed: { 31 | parent.color = Kirigami.Theme.focusColor 32 | 33 | if (Controls.ToolTip.text && !Kirigami.Settings.isMobile) { 34 | Controls.ToolTip.visible = false 35 | } 36 | } 37 | onPressAndHold: { 38 | if (Controls.ToolTip.text && Kirigami.Settings.isMobile) { 39 | remainTooltip = true 40 | Controls.ToolTip.visible = true 41 | } 42 | } 43 | onReleased: { 44 | if (Controls.ToolTip.text && Kirigami.Settings.isMobile) { 45 | if (remainTooltip) { 46 | remainTooltip = false 47 | // TODO: Remain tooltip on mobile devices 48 | Controls.ToolTip.visible = true 49 | } 50 | } 51 | } 52 | onExited: { 53 | parent.color = Kirigami.Theme.textColor 54 | } 55 | onClicked: { 56 | parent.clicked() 57 | 58 | if (containsMouse) { 59 | entered() 60 | } else { 61 | exited() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/qml/elements/ClickableText.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a text that can be clicked. 11 | */ 12 | ScalableText { 13 | property alias mouseArea: mouseArea 14 | signal clicked() 15 | 16 | ClickableMouseArea { 17 | id: mouseArea 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/qml/elements/ConfirmationArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | /** 9 | * This is used for a single action without an instantaneous result. 10 | */ 11 | LoadingStackArea { 12 | default property alias __data: contentArea.data 13 | property alias confirmationButton: confirmationButton 14 | 15 | ColumnLayout { 16 | id: contentArea 17 | spacing: Kirigami.Units.largeSpacing 18 | } 19 | 20 | CenteredAdaptiveHighlightedButton { 21 | id: confirmationButton 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/qml/elements/ContactAdditionDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | Dialog { 11 | id: root 12 | 13 | property alias jid: content.jid 14 | property alias name: content.name 15 | 16 | title: qsTr("Add contact") 17 | padding: Kirigami.Units.mediumSpacing 18 | onOpened: content.jidField.forceActiveFocus() 19 | 20 | ContactAdditionContent { 21 | id: content 22 | jidField.inputField.onActiveFocusChanged: { 23 | // The active focus is taken by another item after opening. 24 | // Thus, it must be forced again. 25 | if (!jidField.inputField.activeFocus && !nameField.inputField.activeFocus && !messageField.activeFocus) { 26 | jidField.forceActiveFocus() 27 | jidField.invalidHintMayBeShown = false 28 | } else { 29 | jidField.invalidHintMayBeShown = true 30 | } 31 | } 32 | } 33 | 34 | Connections { 35 | target: Kaidan 36 | 37 | function onOpenChatPageRequested(accountJid, chatJid) { 38 | root.close() 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/qml/elements/ContactAdditionPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.ScrollablePage { 9 | id: root 10 | 11 | property alias jid: content.jid 12 | property alias name: content.name 13 | 14 | title: qsTr("Add contact") 15 | Component.onCompleted: content.jidField.forceActiveFocus() 16 | 17 | ContactAdditionContent { 18 | id: content 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/qml/elements/ContactAdditionQrCodePage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | 7 | import im.kaidan.kaidan 8 | 9 | /** 10 | * This page is used for adding contacts by scanning QR codes. 11 | */ 12 | ExplanationTogglePage { 13 | id: root 14 | title: qsTr("Add contact") 15 | primaryButton.text: explanationArea.visible ? qsTr("Scan QR codes") : qsTr("Show explanation") 16 | primaryButton.onClicked: { 17 | if (Kaidan.settings.contactAdditionQrCodePageExplanationVisible) { 18 | // Hide the explanation when this page is opened again in the future. 19 | Kaidan.settings.contactAdditionQrCodePageExplanationVisible = false 20 | 21 | if (!qrCodeScanningArea.scanner.cameraEnabled) { 22 | qrCodeScanningArea.scanner.cameraEnabled = true 23 | } 24 | } 25 | } 26 | secondaryButton.visible: false 27 | explanation: ExplanationArea { 28 | primaryExplanationText.text: qsTr("Step 1: Scan your contact's QR code") 29 | primaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-1.svg") 30 | secondaryExplanationText.text: qsTr("Step 2: Let your contact scan your QR code") 31 | secondaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-2.svg") 32 | } 33 | explanationArea.visible: Kaidan.settings.contactAdditionQrCodePageExplanationVisible 34 | content: QrCodeScanningArea { 35 | id: qrCodeScanningArea 36 | accountJid: AccountController.account.jid 37 | anchors.centerIn: parent 38 | visible: !Kaidan.settings.contactAdditionQrCodePageExplanationVisible 39 | } 40 | Component.onCompleted: { 41 | if (!Kaidan.settings.contactAdditionQrCodePageExplanationVisible) { 42 | qrCodeScanningArea.scanner.cameraEnabled = true 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/qml/elements/ContactKeyAuthenticationPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | import org.kde.kirigamiaddons.formcard as FormCard 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This page is used for authenticating encryption keys of contacs by scanning QR codes or entering key IDs. 15 | */ 16 | UserKeyAuthenticationPage { 17 | id: root 18 | explanation: ExplanationArea { 19 | primaryExplanationText.text: qsTr("Step 1: Scan your contact's QR code") 20 | primaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-1.svg") 21 | secondaryExplanationText.text: qsTr("Step 2: Let your contact scan your QR code") 22 | secondaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-2.svg") 23 | } 24 | authenticatableKeysArea.header.title: qsTr("Unverified contact devices") 25 | } 26 | -------------------------------------------------------------------------------- /src/qml/elements/ContactQrCode.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import im.kaidan.kaidan 6 | 7 | QrCode { 8 | id: root 9 | 10 | property string accountJid 11 | 12 | source: qrCodeGenerator.qrCode 13 | 14 | ContactQrCodeGenerator { 15 | id: qrCodeGenerator 16 | accountJid: root.accountJid 17 | jid: root.jid 18 | edgePixelCount: root.width 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/qml/elements/CopyFormTextDelegate.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigamiaddons.formcard as FormCard 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * This is a form text delegate with a button to copy its description. 13 | */ 14 | FormCard.FormTextDelegate { 15 | id: root 16 | background: FormCard.FormDelegateBackground { 17 | control: parent 18 | color: secondaryBackgroundColor 19 | } 20 | trailing: Button { 21 | id: copyButton 22 | Controls.ToolTip.text: qsTr("Copy") 23 | icon.name: "edit-copy-symbolic" 24 | onClicked: Utils.copyToClipboard(root.description) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/qml/elements/CustomContentFormCard.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | import org.kde.kirigamiaddons.formcard as FormCard 8 | 9 | /** 10 | * This is a form card for custom content. 11 | */ 12 | FormCard.FormCard { 13 | default property alias customContent: contentArea.contentItem 14 | property alias title: header.title 15 | 16 | FormCard.FormHeader { 17 | id: header 18 | } 19 | 20 | Kirigami.Separator { 21 | Layout.fillWidth: true 22 | } 23 | 24 | FormCardCustomContentArea { 25 | id: contentArea 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/qml/elements/Dialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a base for a dialog. 11 | */ 12 | Kirigami.Dialog { 13 | id: root 14 | footer: Item {} 15 | preferredWidth: largeButtonWidth 16 | maximumWidth: preferredWidth 17 | maximumHeight: applicationWindow().height - Kirigami.Units.gridUnit * 6 18 | onClosed: destroy() 19 | } 20 | -------------------------------------------------------------------------------- /src/qml/elements/EncryptionKeysArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigamiaddons.formcard as FormCard 9 | 10 | /** 11 | * This area is used to interact with encryption keys. 12 | */ 13 | GridLayout { 14 | property alias header: header 15 | property alias listView: listView 16 | 17 | Layout.maximumHeight: parent.flow === GridLayout.LeftToRight ? parent.height : parent.height / 2 - parent.rowSpacing * 2 18 | 19 | FormCard.FormCard { 20 | Layout.fillWidth: true 21 | Layout.maximumHeight: parent.height 22 | Layout.alignment: Qt.AlignCenter 23 | 24 | FormCard.FormHeader { 25 | id: header 26 | } 27 | 28 | Controls.ScrollView { 29 | Layout.fillWidth: true 30 | Layout.fillHeight: true 31 | Layout.preferredWidth: contentWidth 32 | Layout.preferredHeight: contentHeight 33 | clip: true 34 | 35 | ListView { 36 | id: listView 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/qml/elements/ExplanationOptionsTogglePage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | /** 9 | * This page shows two options and an explanation. 10 | */ 11 | ExplainedContentPage { 12 | required property Item primaryArea 13 | required property Item secondaryArea 14 | property bool explanationInitiallyVisible: true 15 | 16 | primaryButton.onClicked: state = state === "primaryAreaDisplayed" ? "explanationAreaDisplayed" : "primaryAreaDisplayed" 17 | secondaryButton.onClicked: state = state === "secondaryAreaDisplayed" ? "explanationAreaDisplayed" : "secondaryAreaDisplayed" 18 | state: explanationInitiallyVisible ? "explanationAreaDisplayed" : "primaryAreaDisplayed" 19 | states: [ 20 | State { 21 | name: "explanationAreaDisplayed" 22 | PropertyChanges { target: explanationArea; visible: true } 23 | PropertyChanges { target: secondaryArea; visible: false } 24 | }, 25 | State { 26 | name: "primaryAreaDisplayed" 27 | PropertyChanges { target: explanationArea; visible: false } 28 | PropertyChanges { target: primaryArea; visible: true } 29 | PropertyChanges { target: secondaryArea; visible: false } 30 | }, 31 | State { 32 | name: "secondaryAreaDisplayed" 33 | PropertyChanges { target: explanationArea; visible: false } 34 | PropertyChanges { target: primaryArea; visible: false } 35 | PropertyChanges { target: secondaryArea; visible: true } 36 | } 37 | ] 38 | content: Item { 39 | anchors.fill: parent 40 | children: [primaryArea, secondaryArea] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/qml/elements/ExplanationTogglePage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2021 Melvin Keskin 3 | // SPDX-FileCopyrightText: 2023 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | /** 8 | * This is the base for pages having an explained content and a button for toggling the explanation. 9 | */ 10 | ExplainedContentPage { 11 | primaryButton.onClicked: explanationArea.visible = !explanationArea.visible 12 | } 13 | -------------------------------------------------------------------------------- /src/qml/elements/ExtendedMessageContent.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | RowLayout { 11 | id: root 12 | 13 | property alias mainArea: mainArea 14 | property alias mainAreaBackground: mainAreaBackground 15 | property alias contentArea: contentArea 16 | property real minimumWidth 17 | property real maximumWidth 18 | 19 | Controls.Control { 20 | id: mainArea 21 | padding: Kirigami.Units.smallSpacing 22 | background: Rectangle { 23 | id: mainAreaBackground 24 | color: secondaryBackgroundColor 25 | radius: roundedCornersRadius 26 | } 27 | contentItem: RowLayout { 28 | id: contentArea 29 | spacing: Kirigami.Units.smallSpacing * 3 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/qml/elements/FormCardCustomContentArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import org.kde.kirigamiaddons.formcard as FormCard 6 | 7 | /** 8 | * This is an area for custom content to be used in a form card. 9 | */ 10 | FormCard.AbstractFormDelegate { 11 | background: NonInteractiveFormDelegateBackground {} 12 | } 13 | -------------------------------------------------------------------------------- /src/qml/elements/FormComboBoxDelegate.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigamiaddons.formcard as FormCard 9 | 10 | /** 11 | * This is a combo box with a workaround for an array-based model. 12 | */ 13 | FormCard.FormComboBoxDelegate { 14 | // "FormComboBoxDelegate.indexOfValue()" seems to not work with an array-based model. 15 | // Thus, an own function is used. 16 | function indexOf(value) { 17 | if (Array.isArray(model)) { 18 | return model.findIndex((entry) => entry[valueRole] === value) 19 | } 20 | 21 | return indexOfValue(value) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/qml/elements/FormExpansionButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | import org.kde.kirigamiaddons.formcard as FormCard 11 | 12 | /** 13 | * Used to expand and collapse form entries. 14 | */ 15 | FormCard.AbstractFormDelegate { 16 | id: root 17 | leftPadding: 0 18 | rightPadding: 0 19 | checkable: true 20 | contentItem: RowLayout { 21 | Item { 22 | Layout.fillWidth: true 23 | } 24 | 25 | Kirigami.Icon { 26 | source: root.checked ? "go-up-symbolic" : "go-down-symbolic" 27 | implicitWidth: Kirigami.Units.iconSizes.small 28 | implicitHeight: implicitWidth 29 | } 30 | 31 | Item { 32 | Layout.fillWidth: true 33 | } 34 | } 35 | Layout.fillWidth: true 36 | } 37 | -------------------------------------------------------------------------------- /src/qml/elements/FormInfoContent.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | Controls.Control { 11 | default property alias __data: mainArea.data 12 | 13 | topPadding: Kirigami.Settings.isMobile ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 3 14 | bottomPadding: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing * 3 15 | leftPadding: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing * 2 16 | rightPadding: leftPadding 17 | background: Rectangle { 18 | color: secondaryBackgroundColor 19 | } 20 | contentItem: ColumnLayout { 21 | id: mainArea 22 | spacing: Kirigami.Units.largeSpacing 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/qml/elements/FormInfoDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | Dialog { 9 | default property alias __data: mainArea.data 10 | 11 | preferredWidth: Kirigami.Units.gridUnit * 33 12 | maximumWidth: preferredWidth 13 | topPadding: 0 14 | bottomPadding: 0 15 | leftPadding: 0 16 | rightPadding: 0 17 | 18 | ColumnLayout { 19 | id: mainArea 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/elements/FormInfoHeader.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | GridLayout { 9 | flow: width > Kirigami.Units.gridUnit * 20 ? GridLayout.LeftToRight : GridLayout.TopToBottom 10 | Layout.topMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.smallSpacing * 5 11 | Layout.bottomMargin: Kirigami.Settings.isMobile ? 0 : Kirigami.Units.largeSpacing * 2 12 | Layout.leftMargin: Kirigami.Units.smallSpacing * 5 13 | Layout.rightMargin: Layout.leftMargin 14 | } 15 | -------------------------------------------------------------------------------- /src/qml/elements/FormInfoPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.ScrollablePage { 9 | default property alias __data: mainArea.data 10 | 11 | leftPadding: 0 12 | rightPadding: 0 13 | Kirigami.Theme.colorSet: Kirigami.Theme.Window 14 | 15 | ColumnLayout { 16 | id: mainArea 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/qml/elements/FormattedTextArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * Area for displaying correctly formatted text including proper emojis. 13 | * 14 | * This is intended to enter text. 15 | * For displaying text only, use FormattedTextEdit. 16 | */ 17 | Controls.TextArea { 18 | id: root 19 | wrapMode: Text.Wrap 20 | 21 | TextFormatter { 22 | textDocument: root.textDocument 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/qml/elements/FormattedTextEdit.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | /** 11 | * Correctly formatted text including proper emojis and links. 12 | * 13 | * This is intended to display text. 14 | * For entering text, use FormattedTextArea. 15 | */ 16 | TextEdit { 17 | id: root 18 | 19 | // Whether to apply an enhanced formatting (e.g., single emojis are enlarged and links are 20 | // marked as such) 21 | property alias enhancedFormatting: formatter.enhancedFormatting 22 | 23 | color: Kirigami.Theme.textColor 24 | enabled: false 25 | wrapMode: Text.Wrap 26 | readOnly: true 27 | activeFocusOnPress: false 28 | onLinkActivated: Qt.openUrlExternally(link) 29 | 30 | HoverHandler { 31 | id: hoverHandler 32 | cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor 33 | } 34 | 35 | TextFormatter { 36 | id: formatter 37 | textDocument: root.textDocument 38 | enhancedFormatting: false 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/qml/elements/GeoLocationMapPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.Page { 9 | id: root 10 | property alias coordinate: map.center 11 | 12 | title: qsTr("Show location") 13 | padding: 0 14 | 15 | GeoLocationMap { 16 | id: map 17 | zoomLevelFactor: 0.55 18 | anchors.fill: parent 19 | Component.onCompleted: userPositionMarker.coordinate = center 20 | 21 | ZoomSlider { 22 | from: parent.minimumZoomLevel 23 | to: parent.maximumZoomLevel 24 | value: parent.zoomLevel 25 | width: parent.width - Kirigami.Units.gridUnit * 16 26 | onValueChanged: parent.zoomLevel = value 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/qml/elements/GeoLocationMapPreview.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | GeoLocationMap { 9 | id: root 10 | 11 | property Item message 12 | 13 | layer.enabled: GraphicsInfo.api !== GraphicsInfo.Software 14 | layer.effect: Kirigami.ShadowedTexture { 15 | radius: roundedCornersRadius 16 | } 17 | 18 | Behavior on opacity { 19 | NumberAnimation {} 20 | } 21 | 22 | OpacityChangingMouseArea { 23 | // Keep the copyright link accessible. 24 | z: parent.z - 1 25 | opacityItem: parent 26 | acceptedButtons: Qt.LeftButton | Qt.RightButton 27 | onClicked: (event) => { 28 | if (event.button === Qt.LeftButton) { 29 | message.openGeoLocationMap() 30 | } else if (event.button === Qt.RightButton) { 31 | root.message.showContextMenu(this) 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/qml/elements/GeoLocationPreview.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | ExtendedMessageContent { 13 | id: root 14 | 15 | property Item message 16 | 17 | mainArea.data: OpacityChangingMouseArea { 18 | opacityItem: parent.background 19 | acceptedButtons: Qt.LeftButton | Qt.RightButton 20 | onClicked: (event) => { 21 | if (event.button === Qt.LeftButton) { 22 | message.openGeoLocationMap() 23 | } else if (event.button === Qt.RightButton) { 24 | root.message.showContextMenu(this) 25 | } 26 | } 27 | } 28 | mainAreaBackground { 29 | Behavior on opacity { 30 | NumberAnimation {} 31 | } 32 | } 33 | contentArea.data: [ 34 | Kirigami.Icon { 35 | id: icon 36 | source: "mark-location-symbolic" 37 | Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium 38 | }, 39 | 40 | Controls.Label { 41 | text: qsTr("Location") 42 | wrapMode: Text.Wrap 43 | Layout.minimumWidth: root.minimumWidth - mainArea.leftPadding - icon.width - contentArea.spacing - mainArea.rightPadding 44 | Layout.maximumWidth: root.maximumWidth - mainArea.leftPadding - icon.width - contentArea.spacing - mainArea.rightPadding 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatContactInvitationItem.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | UserListItem { 11 | id: root 12 | 13 | // left: name 14 | Kirigami.Heading { 15 | text: root.name 16 | textFormat: Text.PlainText 17 | elide: Text.ElideRight 18 | maximumLineCount: 1 19 | level: 4 20 | Layout.fillWidth: true 21 | } 22 | 23 | // right: selection marker 24 | UserListItemSelectionMarker { 25 | userListItem: root 26 | Layout.rightMargin: Kirigami.Units.largeSpacing * 2 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatCreationDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | Dialog { 11 | id: root 12 | 13 | property alias accountJid: content.accountJid 14 | property alias groupChatName: content.groupChatName 15 | property alias nickname: content.nickname 16 | 17 | title: qsTr("Create group chat") 18 | padding: Kirigami.Units.mediumSpacing 19 | onOpened: content.groupChatNameField.forceActiveFocus() 20 | 21 | GroupChatCreationContent { 22 | id: content 23 | groupChatNameField.inputField.onActiveFocusChanged: { 24 | // The active focus is taken by another item after opening. 25 | // Thus, it must be forced again. 26 | if (!groupChatNameField.inputField.activeFocus && !groupChatIdField.inputField.activeFocus && !nicknameField.inputField.activeFocus) { 27 | groupChatNameField.forceActiveFocus() 28 | groupChatNameField.invalidHintMayBeShown = false 29 | } else { 30 | groupChatNameField.invalidHintMayBeShown = true 31 | } 32 | } 33 | } 34 | 35 | Connections { 36 | target: Kaidan 37 | 38 | function onOpenChatPageRequested(accountJid, chatJid) { 39 | root.close() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatCreationPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.ScrollablePage { 9 | id: root 10 | 11 | property alias accountJid: content.accountJid 12 | property alias groupChatName: content.groupChatName 13 | property alias nickname: content.nickname 14 | 15 | title: qsTr("Create group chat") 16 | Component.onCompleted: content.groupChatNameField.forceActiveFocus() 17 | 18 | GroupChatCreationContent { 19 | id: content 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatInvitation.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | ExtendedMessageContent { 13 | id: root 14 | 15 | property Item message 16 | 17 | mainArea.data: OpacityChangingMouseArea { 18 | opacityItem: parent.background 19 | acceptedButtons: Qt.LeftButton | Qt.RightButton 20 | onClicked: (event) => { 21 | if (event.button === Qt.LeftButton) { 22 | const chatJid = root.message.groupChatInvitationJid 23 | 24 | if (groupChatWatcher.item.isGroupChat) { 25 | Kaidan.openChatPageRequested(ChatController.accountJid, chatJid) 26 | } else { 27 | openView(groupChatJoiningDialog, groupChatJoiningPage).groupChatJid = chatJid 28 | } 29 | } else if (event.button === Qt.RightButton) { 30 | root.message.showContextMenu(this) 31 | } 32 | } 33 | } 34 | mainAreaBackground { 35 | Behavior on opacity { 36 | NumberAnimation {} 37 | } 38 | } 39 | contentArea.data: [ 40 | Kirigami.Icon { 41 | id: icon 42 | source: "system-users-symbolic" 43 | Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium 44 | }, 45 | 46 | Controls.Label { 47 | text: root.message.messageBody 48 | wrapMode: Text.Wrap 49 | Layout.minimumWidth: root.minimumWidth - mainArea.leftPadding - icon.width - contentArea.spacing - mainArea.rightPadding 50 | Layout.maximumWidth: root.maximumWidth - mainArea.leftPadding - icon.width - contentArea.spacing - mainArea.rightPadding 51 | } 52 | ] 53 | 54 | RosterItemWatcher { 55 | id: groupChatWatcher 56 | jid: root.message.groupChatInvitationJid 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatJoiningDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | import im.kaidan.kaidan 9 | 10 | Dialog { 11 | id: root 12 | 13 | property alias accountJid: content.accountJid 14 | property alias groupChatJid: content.groupChatJid 15 | property alias nickname: content.nickname 16 | 17 | title: qsTr("Join group chat") 18 | padding: Kirigami.Units.mediumSpacing 19 | onOpened: content.groupChatJidField.forceActiveFocus() 20 | 21 | GroupChatJoiningContent { 22 | id: content 23 | groupChatJidField.inputField.onActiveFocusChanged: { 24 | // The active focus is taken by another item after opening. 25 | // Thus, it must be forced again. 26 | if (!groupChatJidField.inputField.activeFocus && !nicknameField.inputField.activeFocus) { 27 | groupChatJidField.forceActiveFocus() 28 | groupChatJidField.invalidHintMayBeShown = false 29 | } else { 30 | groupChatJidField.invalidHintMayBeShown = true 31 | } 32 | } 33 | } 34 | 35 | Connections { 36 | target: Kaidan 37 | 38 | function onOpenChatPageRequested(accountJid, chatJid) { 39 | root.close() 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatJoiningPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.ScrollablePage { 9 | id: root 10 | 11 | property alias accountJid: content.accountJid 12 | property alias groupChatJid: content.groupChatJid 13 | property alias nickname: content.nickname 14 | 15 | title: qsTr("Join group chat") 16 | Component.onCompleted: content.groupChatJidField.forceActiveFocus() 17 | 18 | GroupChatJoiningContent { 19 | id: content 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatQrCode.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import im.kaidan.kaidan 6 | 7 | QrCode { 8 | id: root 9 | source: qrCodeGenerator.qrCode 10 | 11 | GroupChatQrCodeGenerator { 12 | id: qrCodeGenerator 13 | jid: root.jid 14 | edgePixelCount: root.width 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatUserItem.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | UserListItem { 13 | id: root 14 | 15 | property alias userText: userText 16 | 17 | Controls.Label { 18 | id: userText 19 | text: root.name 20 | textFormat: Text.PlainText 21 | elide: Text.ElideMiddle 22 | maximumLineCount: 1 23 | Layout.fillWidth: true 24 | leftPadding: Kirigami.Units.smallSpacing * 1.5 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/qml/elements/GroupChatUserKeyAuthenticationPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | import org.kde.kirigamiaddons.formcard as FormCard 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This page is used for authenticating encryption keys of group chat users by scanning QR codes or entering key IDs. 15 | */ 16 | UserKeyAuthenticationPage { 17 | id: root 18 | explanation: ExplanationArea { 19 | primaryExplanationText.text: qsTr("Step 1: Scan the participant's QR code") 20 | primaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-1.svg") 21 | secondaryExplanationText.text: qsTr("Step 2: Let the participant scan your QR code") 22 | secondaryExplanationImage.source: Utils.getResourcePath("images/qr-code-scan-2.svg") 23 | } 24 | authenticatableKeysArea.header.title: qsTr("Unverified participant devices") 25 | } 26 | -------------------------------------------------------------------------------- /src/qml/elements/HighlightedFormButtonBackground.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import org.kde.kirigami as Kirigami 6 | import org.kde.kirigamiaddons.formcard as FormCard 7 | 8 | /** 9 | * This is a highlighted background with a dynamic color for a form delegate. 10 | */ 11 | FormCard.FormDelegateBackground { 12 | control: parent 13 | color: { 14 | let colorOpacity = 0.1; 15 | 16 | if (!control.enabled) { 17 | colorOpacity = 0; 18 | } else if (control.pressed) { 19 | colorOpacity = 0.2; 20 | } else if (control.visualFocus) { 21 | colorOpacity = 0.1; 22 | } else if (!Kirigami.Settings.tabletMode && control.hovered) { 23 | colorOpacity = 0.15; 24 | } 25 | 26 | const highlightColor = Kirigami.Theme.highlightColor 27 | return Qt.rgba(highlightColor.r, highlightColor.g, highlightColor.b, colorOpacity) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/qml/elements/HorizontalSeparator.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | Kirigami.Separator { 9 | visible: !Kirigami.Settings.isMobile 10 | implicitHeight: Kirigami.Units.smallSpacing 11 | Layout.fillWidth: true 12 | } 13 | -------------------------------------------------------------------------------- /src/qml/elements/ImageCaptureDialog.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2019 Filipe Azevedo 2 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 4 | // SPDX-FileCopyrightText: 2021 Linus Jahn 5 | // 6 | // SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | import QtQuick 9 | import QtQuick.Controls as Controls 10 | import QtMultimedia 11 | import org.kde.kirigami as Kirigami 12 | 13 | import im.kaidan.kaidan 14 | 15 | NewMediaDialog { 16 | id: root 17 | title: qsTr("Take a picture") 18 | captureSession.imageCapture: ImageCapture {} 19 | shutterRelease { 20 | iconSource: "camera-photo-symbolic" 21 | onClicked: captureSession.imageCapture.captureToFile(MediaUtils.localFilePath(MediaUtils.newImageFileUrl())) 22 | } 23 | 24 | Connections { 25 | target: root.captureSession.imageCapture 26 | ignoreUnknownSignals: true 27 | 28 | function onImageSaved(requestId, path) { 29 | root.addFile(MediaUtils.localFileUrl(path)) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/qml/elements/InlineListView.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | 7 | /** 8 | * This is a list view that can be placed inside of a flickable element. 9 | * 10 | * That way, the list view is not moved out of the parent element when the list view is flicked. 11 | */ 12 | ListView { 13 | interactive: false 14 | clip: true 15 | } 16 | -------------------------------------------------------------------------------- /src/qml/elements/ListViewSearchField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | 8 | /** 9 | * This is a search field for filtering a list view. 10 | */ 11 | Kirigami.SearchField { 12 | id: root 13 | 14 | property ListView listView 15 | 16 | Keys.onEscapePressed: clear() 17 | Keys.onPressed: event => { 18 | switch (event.key) { 19 | case Qt.Key_Escape: 20 | clear() 21 | break 22 | case Qt.Key_Return: 23 | case Qt.Key_Enter: 24 | if (listView.count > 0) { 25 | // Simulate clicking on the first item of the listView. 26 | listView.itemAtIndex(0).clicked() 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/qml/elements/ListViewSectionDelegate.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import org.kde.kirigami as Kirigami 7 | import org.kde.kirigamiaddons.formcard as FormCard 8 | 9 | FormCard.FormSectionText { 10 | text: section 11 | padding: Kirigami.Units.largeSpacing 12 | leftPadding: Kirigami.Units.largeSpacing * 3 13 | font.weight: Font.Light 14 | anchors.left: parent.left 15 | anchors.right: parent.right 16 | background: Rectangle { 17 | color: tertiaryBackgroundColor 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/qml/elements/LoadingArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | Item { 11 | id: root 12 | 13 | property alias background: background 14 | property alias description: description.text 15 | 16 | RoundedRectangle { 17 | id: background 18 | anchors.fill: content 19 | anchors.margins: -8 20 | color: primaryBackgroundColor 21 | opacity: 0.9 22 | } 23 | 24 | ColumnLayout { 25 | id: content 26 | anchors.centerIn: parent 27 | 28 | Controls.BusyIndicator { 29 | Layout.alignment: Qt.AlignHCenter 30 | } 31 | 32 | Controls.Label { 33 | id: description 34 | text: qsTr("Loading…") 35 | font.italic: true 36 | color: Kirigami.Theme.textColor 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/qml/elements/LoadingStackArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick.Layouts 6 | import org.kde.kirigami as Kirigami 7 | 8 | /** 9 | * This is used for actions without instantaneous results. 10 | */ 11 | StackLayout { 12 | default property alias __data: contentArea.data 13 | property alias loadingArea: loadingArea 14 | property bool busy: false 15 | 16 | currentIndex: busy ? 1 : 0 17 | 18 | ColumnLayout { 19 | id: contentArea 20 | spacing: Kirigami.Units.largeSpacing * 2 21 | } 22 | 23 | LoadingArea { 24 | id: loadingArea 25 | background.color: secondaryBackgroundColor 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/qml/elements/LoginQrCode.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import im.kaidan.kaidan 6 | 7 | QrCode { 8 | id: root 9 | source: qrCodeGenerator.qrCode 10 | 11 | LoginQrCodeGenerator { 12 | id: qrCodeGenerator 13 | jid: root.jid 14 | edgePixelCount: root.width 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/qml/elements/MediumSendingPreview.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | MediumPreview { 11 | id: root 12 | 13 | property var selectionModel 14 | property var modelData 15 | 16 | name: modelData.name 17 | description: modelData.description 18 | size: modelData.size 19 | localFileUrl: modelData.localFileUrl 20 | type: modelData.type 21 | minimumDetailsWidth: minimumWidth - detailsWidthLimitBase - root.spacing - mediumRemovalButton.width 22 | maximumDetailsWidth: maximumWidth - detailsWidthLimitBase - root.spacing - mediumRemovalButton.width 23 | mainArea.rightPadding: Kirigami.Units.smallSpacing * 3 24 | previewImage { 25 | source: modelData.previewImage 26 | data: OpacityChangingMouseArea { 27 | opacityItem: parent 28 | onClicked: root.open() 29 | } 30 | 31 | Behavior on opacity { 32 | NumberAnimation {} 33 | } 34 | } 35 | detailsArea.data: FormattedTextArea { 36 | text: root.description 37 | placeholderText: qsTr("Description") 38 | Layout.minimumWidth: root.minimumDetailsWidth 39 | Layout.maximumWidth: root.maximumDetailsWidth 40 | Component.onCompleted: { 41 | if (!root.description) { 42 | visible = true 43 | } 44 | } 45 | onTextChanged: modelData.description = text 46 | } 47 | 48 | ClickableIcon { 49 | id: mediumRemovalButton 50 | Controls.ToolTip.text: qsTr("Remove file") 51 | source: "window-close-symbolic" 52 | Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium 53 | onClicked: root.selectionModel.removeFile(root.modelData.index) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/qml/elements/MessageCounter.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2017 Linus Jahn 2 | // SPDX-FileCopyrightText: 2019 Robert Maerkisch 3 | // SPDX-FileCopyrightText: 2021 Melvin Keskin 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | Controls.Label { 12 | property int count: 0 13 | property bool muted: false 14 | 15 | text: count > 9999 ? "9999+" : count 16 | color: Kirigami.Theme.backgroundColor 17 | font.pixelSize: Kirigami.Theme.defaultFont.pixelSize * 0.9 18 | font.weight: Font.DemiBold 19 | visible: count 20 | leftPadding: font.pixelSize * 0.45 21 | rightPadding: leftPadding 22 | topPadding: leftPadding * 0.3 23 | bottomPadding: topPadding 24 | 25 | background: Rectangle { 26 | radius: parent.height * 0.5 27 | color: parent.muted ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.positiveTextColor 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/qml/elements/MessageReactionAdditionButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This is a button for opening an emoji picker to add emojis in reaction to a message. 15 | */ 16 | MessageReactionButton { 17 | id: root 18 | 19 | property string messageId 20 | property bool isOwnMessage 21 | property MessageReactionEmojiPicker emojiPicker 22 | 23 | text: qsTr("Add reaction…") 24 | primaryColor: isOwnMessage ? primaryBackgroundColor : secondaryBackgroundColor 25 | contentItem: Kirigami.Icon { 26 | source: "smiley-add" 27 | } 28 | onClicked: { 29 | emojiPicker.messageId = root.messageId 30 | emojiPicker.open() 31 | } 32 | Controls.ToolTip.text: text 33 | } 34 | -------------------------------------------------------------------------------- /src/qml/elements/MessageReactionButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This is a button used for a reaction or for reacting to a message. 15 | */ 16 | Button { 17 | id: root 18 | 19 | property color primaryColor 20 | property color accentColor 21 | 22 | height: Kirigami.Theme.defaultFont.pixelSize * 2.1 23 | width: smallButtonWidth 24 | hoverEnabled: true 25 | background: RoundedRectangle { 26 | color: { 27 | if (root.hovered) { 28 | return Qt.tint(root.primaryColor, Qt.rgba(root.accentColor.r, root.accentColor.g, root.accentColor.b, 0.1)) 29 | } 30 | 31 | return Qt.tint(root.primaryColor, Qt.rgba(root.accentColor.r, root.accentColor.g, root.accentColor.b, 0.5)) 32 | } 33 | 34 | Behavior on color { 35 | ColorAnimation { duration: Kirigami.Units.shortDuration } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/qml/elements/MessageReactionDetailsButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | /** 13 | * This is a button for opening the details of all emojis in reaction to a message. 14 | */ 15 | MessageReactionButton { 16 | id: root 17 | 18 | property string messageId 19 | property bool isOwnMessage 20 | property var reactions 21 | 22 | text: qsTr("Show details") 23 | primaryColor: isOwnMessage ? primaryBackgroundColor : secondaryBackgroundColor 24 | contentItem: Kirigami.Icon { 25 | source: "view-more-symbolic" 26 | } 27 | onClicked: { 28 | var detailsDialog = openOverlay(detailsDialogComponent) 29 | detailsDialog.messageId = messageId 30 | detailsDialog.reactions = reactions 31 | detailsDialog.open() 32 | } 33 | onReactionsChanged: { 34 | if (detailsDialog.messageId === messageId) { 35 | detailsDialog.reactions = reactions 36 | } 37 | } 38 | Controls.ToolTip.text: text 39 | 40 | Component { 41 | id: detailsDialogComponent 42 | 43 | MessageReactionDetailsDialog { 44 | accountJid: ChatController.accountJid 45 | chatJid: ChatController.chatJid 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/qml/elements/MessageReactionEmojiPicker.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This is an emoji picker for adding emojis in reaction to a message. 15 | */ 16 | EmojiPicker { 17 | id: root 18 | 19 | property string messageId 20 | 21 | parent: root.parent 22 | anchors.centerIn: parent 23 | textArea: Controls.TextArea { 24 | visible: false 25 | onTextChanged: { 26 | // TODO: Refactor EmojiPicker to not append trailing whitespaces in this case 27 | if (text.length) { 28 | MessageModel.addMessageReaction(root.messageId, text.trim()) 29 | } 30 | clear() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/qml/elements/MessageReactionRetryButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import QtQuick.Controls as Controls 9 | import org.kde.kirigami as Kirigami 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This is a button for opening an emoji picker to add emojis in reaction to a message. 15 | */ 16 | MessageReactionButton { 17 | id: root 18 | 19 | property string messageId 20 | property bool isOwnMessage 21 | 22 | text: qsTr("Retry updating reactions") 23 | primaryColor: isOwnMessage ? primaryBackgroundColor : secondaryBackgroundColor 24 | // Unlike the other items inheriting MessageReactionButton, the icon would be too large without 25 | // the padding here. 26 | padding: Kirigami.Units.smallSpacing 27 | contentItem: Kirigami.Icon { 28 | source: "view-refresh-symbolic" 29 | } 30 | onClicked: MessageModel.resendMessageReactions(root.messageId) 31 | Controls.ToolTip.text: text 32 | } 33 | -------------------------------------------------------------------------------- /src/qml/elements/NonInteractiveFormDelegateBackground.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import org.kde.kirigamiaddons.formcard as FormCard 6 | 7 | /** 8 | * This is a background with a static color for a form delegate. 9 | */ 10 | FormCard.FormDelegateBackground { 11 | control: parent 12 | color: secondaryBackgroundColor 13 | } 14 | -------------------------------------------------------------------------------- /src/qml/elements/OpacityChangingMouseArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | 7 | /** 8 | * This is placed inside of an item to change the opacity of opacityItem when the item is hovered. 9 | */ 10 | MouseArea { 11 | property Item opacityItem 12 | 13 | hoverEnabled: true 14 | anchors.fill: parent 15 | onContainsMouseChanged: opacityItem.opacity = containsMouse ? 0.5 : 1 16 | } 17 | -------------------------------------------------------------------------------- /src/qml/elements/QrCode.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2021 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | RoundedImage { 13 | property string jid 14 | } 15 | -------------------------------------------------------------------------------- /src/qml/elements/RecordingIndicator.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | import im.kaidan.kaidan 11 | 12 | /** 13 | * This is an indicator for an ongoing audio/video recording. 14 | */ 15 | RowLayout { 16 | required property int duration 17 | 18 | Behavior on opacity { 19 | NumberAnimation {} 20 | } 21 | 22 | Kirigami.Icon { 23 | source: "media-record-symbolic" 24 | color: Kirigami.Theme.negativeTextColor 25 | isMask: true 26 | Layout.preferredWidth: Kirigami.Units.iconSizes.small 27 | 28 | Timer { 29 | interval: Kirigami.Units.veryLongDuration 30 | repeat: true 31 | running: true 32 | onTriggered: parent.opacity = 1 - parent.opacity 33 | } 34 | } 35 | 36 | Controls.Label { 37 | text: MediaUtils.prettyDuration(parent.duration) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/qml/elements/RoundedImage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Jonah Brüchert 2 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * Image with rounded corners. 11 | */ 12 | Kirigami.Icon { 13 | id: root 14 | 15 | property real radius: relativeRoundedCornersRadius(width, height) 16 | 17 | layer.enabled: GraphicsInfo.api !== GraphicsInfo.Software 18 | layer.effect: Kirigami.ShadowedTexture { 19 | radius: root.radius 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/qml/elements/RoundedRectangle.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | 7 | /** 8 | * This is a rectangle with rounded corners having a radius relative to the rectangle's dimensions. 9 | * 10 | * It should be used to maintain a consistent look of the corners fitting the rectangle's size. 11 | */ 12 | Rectangle { 13 | radius: relativeRoundedCornersRadius(width, height) 14 | } 15 | -------------------------------------------------------------------------------- /src/qml/elements/ScalableText.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a text that can be scaled. 11 | */ 12 | Controls.Label { 13 | // factor to scale the text 14 | property double scaleFactor: 1 15 | 16 | font.pixelSize: Kirigami.Theme.defaultFont.pixelSize * scaleFactor 17 | } 18 | -------------------------------------------------------------------------------- /src/qml/elements/SelectablePreview.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | 10 | /** 11 | * Used to show a selectable preview (e.g., of an image or video). 12 | */ 13 | Controls.ItemDelegate { 14 | id: root 15 | 16 | default property alias __data: selectionArea.data 17 | property alias containsMouse: selectionArea.containsMouse 18 | 19 | implicitWidth: GridView.view.cellWidth 20 | implicitHeight: GridView.view.cellHeight 21 | autoExclusive: false 22 | topPadding: 0 23 | bottomPadding: 0 24 | leftPadding: 0 25 | rightPadding: 0 26 | background: Rectangle {} 27 | contentItem: MouseArea { 28 | id: selectionArea 29 | hoverEnabled: true 30 | acceptedButtons: Qt.NoButton 31 | 32 | // overlay to indicate the currently hovered or selected medium 33 | Rectangle { 34 | color: Kirigami.Theme.highlightColor 35 | opacity: 0.1 36 | z: 1 37 | visible: root.hovered || root.checked 38 | anchors.fill: parent 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/qml/elements/SelectionMarker.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * Used to select an item inside of it. 13 | */ 14 | Controls.CheckBox { 15 | id: root 16 | padding: 3 17 | implicitWidth: 2 * padding + Math.max(implicitIndicatorWidth, implicitBackgroundWidth) 18 | implicitHeight: 2 * padding + Math.max(implicitIndicatorHeight, implicitBackgroundHeight) 19 | 20 | background: Rectangle { 21 | color: Kirigami.Theme.highlightColor 22 | opacity: root.hovered ? 1 : 0.8 23 | implicitWidth: 20 24 | implicitHeight: 20 25 | radius: implicitWidth 26 | } 27 | 28 | indicator: Kirigami.Icon { 29 | source: "emblem-ok-symbolic" 30 | color: Kirigami.Theme.highlightedTextColor 31 | isMask: true 32 | implicitWidth: Kirigami.Units.iconSizes.small 33 | implicitHeight: implicitWidth 34 | anchors.centerIn: parent 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/qml/elements/ShutterRelease.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | Controls.ItemDelegate { 12 | id: root 13 | 14 | property var iconSource 15 | 16 | background: Kirigami.ShadowedRectangle { 17 | color: { 18 | if (parent.pressed) { 19 | return Qt.darker(secondaryBackgroundColor, 1.05) 20 | } 21 | 22 | return parent.hovered ? secondaryBackgroundColor : primaryBackgroundColor 23 | } 24 | opacity: 0.9 25 | radius: height / 2 26 | shadow.color: Qt.darker(color, 1.2) 27 | shadow.size: 4 28 | 29 | Behavior on color { 30 | ColorAnimation { 31 | duration: Kirigami.Units.shortDuration 32 | } 33 | } 34 | } 35 | contentItem: Loader { 36 | sourceComponent: parent.enabled ? icon : busyIndicator 37 | 38 | Component { 39 | id: icon 40 | 41 | Kirigami.Icon { 42 | source: root.iconSource 43 | } 44 | } 45 | 46 | Component { 47 | id: busyIndicator 48 | 49 | Controls.BusyIndicator {} 50 | } 51 | } 52 | padding: width * 0.2 53 | width: Kirigami.Units.iconSizes.large 54 | height: width 55 | } 56 | -------------------------------------------------------------------------------- /src/qml/elements/SimpleListViewSearchField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | /** 6 | * This is a search field for filtering a list view via a simple search. 7 | */ 8 | ListViewSearchField { 9 | id: root 10 | onTextChanged: listView.model.setFilterFixedString(text.toLowerCase()) 11 | } 12 | -------------------------------------------------------------------------------- /src/qml/elements/Slider.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a slider with a custom background and handle. 11 | */ 12 | Controls.Slider { 13 | id: root 14 | from: 0 15 | implicitHeight: Kirigami.Units.iconSizes.small 16 | background: SliderBackground { 17 | visualPosition: parent.visualPosition 18 | } 19 | handle: Rectangle { 20 | x: parent.leftPadding + parent.visualPosition * (parent.availableWidth - width) 21 | color: { 22 | const baseColor = Kirigami.Theme.activeTextColor 23 | 24 | if (parent.pressed) { 25 | return Qt.darker(baseColor, 1.4) 26 | } 27 | 28 | if (parent.hovered) { 29 | return Qt.darker(baseColor, 1.2) 30 | } 31 | 32 | return baseColor 33 | } 34 | radius: height / 2 35 | implicitWidth: Kirigami.Units.iconSizes.small 36 | implicitHeight: implicitWidth 37 | anchors.verticalCenter: parent.verticalCenter 38 | 39 | Behavior on color { 40 | ColorAnimation {} 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/qml/elements/SliderBackground.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a custom background used for a slider. 11 | */ 12 | Rectangle { 13 | id: root 14 | 15 | required property real visualPosition 16 | 17 | color: Kirigami.Theme.activeBackgroundColor 18 | radius: height / 2 19 | height: implicitHeight 20 | implicitHeight: Kirigami.Units.smallSpacing 21 | anchors.verticalCenter: parent.verticalCenter 22 | 23 | Rectangle { 24 | color: Kirigami.Theme.activeTextColor 25 | opacity: 0.8 26 | radius: parent.radius 27 | width: root.visualPosition * parent.width 28 | height: parent.height 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/qml/elements/UrlFormButtonDelegate.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigamiaddons.formcard as FormCard 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * This is a form button delegate for a URL. 13 | * 14 | * Its main purpose is to open the URL externally when the button is clicked. 15 | * It includes a secondary button for copying the URL instead of opening it. 16 | */ 17 | FormCard.FormTextDelegate { 18 | id: root 19 | 20 | property url url 21 | property alias copyButton: copyButton 22 | 23 | background: FormCard.FormDelegateBackground { 24 | control: parent 25 | } 26 | trailing: Button { 27 | id: copyButton 28 | Controls.ToolTip.text: qsTr("Copy web address") 29 | icon.name: "edit-copy-symbolic" 30 | onClicked: Utils.copyToClipboard(root.url) 31 | } 32 | onClicked: Qt.openUrlExternally(url) 33 | } 34 | -------------------------------------------------------------------------------- /src/qml/elements/UserKeyAuthenticationPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Layouts 7 | import QtQuick.Controls as Controls 8 | import org.kde.kirigami as Kirigami 9 | import org.kde.kirigamiaddons.formcard as FormCard 10 | 11 | import im.kaidan.kaidan 12 | 13 | /** 14 | * This page is used for authenticating encryption keys of users by scanning QR codes or entering key IDs. 15 | */ 16 | KeyAuthenticationPage { 17 | id: root 18 | authenticatableKeysArea.listView.model: AuthenticatableEncryptionKeyModel { 19 | accountJid: root.accountJid 20 | chatJid: root.jid 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/qml/elements/UserListItemSelectionMarker.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * Used to select a UserlistItem inside of it. 13 | */ 14 | Rectangle { 15 | property Item userListItem 16 | 17 | color: { 18 | let colorOpacity = 0; 19 | 20 | if (userListItem.pressed) { 21 | colorOpacity = 0.35 22 | } else if (userListItem.selected) { 23 | if (userListItem.hovered) { 24 | colorOpacity = 0.3 25 | } else { 26 | colorOpacity = 0.25 27 | } 28 | } else if (userListItem.hovered) { 29 | colorOpacity = 0.1 30 | } 31 | 32 | const textColor = Kirigami.Theme.textColor 33 | return Qt.rgba(textColor.r, textColor.g, textColor.b, colorOpacity) 34 | } 35 | implicitWidth: Kirigami.Units.iconSizes.smallMedium 36 | implicitHeight: Kirigami.Units.iconSizes.smallMedium 37 | radius: implicitWidth 38 | 39 | Behavior on color { 40 | ColorAnimation { duration: Kirigami.Units.shortDuration } 41 | } 42 | 43 | Kirigami.Icon { 44 | source: "emblem-ok-symbolic" 45 | color: Kirigami.Theme.backgroundColor 46 | implicitWidth: Kirigami.Units.iconSizes.small 47 | implicitHeight: Kirigami.Units.iconSizes.small 48 | anchors.centerIn: parent 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/qml/elements/ZoomSlider.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | import QtQuick.Controls as Controls 7 | import org.kde.kirigami as Kirigami 8 | 9 | /** 10 | * This is a slider to zoom in/out to be used as an overlay on top of a visual item. 11 | */ 12 | Slider { 13 | id: root 14 | value: 1 15 | from: 1 16 | to: 3 17 | width: parent.width - Kirigami.Units.largeSpacing * 4 18 | horizontalPadding: Kirigami.Units.largeSpacing 19 | anchors.bottom: parent.bottom 20 | anchors.bottomMargin: Kirigami.Units.largeSpacing * 3 21 | anchors.horizontalCenter: parent.horizontalCenter 22 | background: Rectangle { 23 | color: primaryBackgroundColor 24 | opacity: 0.9 25 | radius: relativeRoundedCornersRadius(parent.width, parent.height) * 3 26 | height: implicitHeight 27 | implicitHeight: Kirigami.Units.largeSpacing * 4 28 | anchors.verticalCenter: parent.verticalCenter 29 | 30 | SliderBackground { 31 | visualPosition: root.visualPosition 32 | width: parent.width - root.horizontalPadding * 2 33 | anchors.left: parent.left 34 | anchors.leftMargin: root.horizontalPadding 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/qml/elements/fields/CredentialsField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | 8 | import im.kaidan.kaidan 9 | 10 | /** 11 | * This is the base for fields which are used to enter credentials. 12 | */ 13 | Field { 14 | property alias credentialsValidator: credentialsValidator 15 | 16 | CredentialsValidator { 17 | id: credentialsValidator 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/qml/elements/fields/JidField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2020 Linus Jahn 3 | // SPDX-FileCopyrightText: 2023 Filipe Azevedo 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | import QtQuick 8 | 9 | import im.kaidan.kaidan 10 | 11 | import ".." 12 | 13 | /** 14 | * This is a JID field with a hint for invalid JIDs. 15 | */ 16 | CredentialsField { 17 | id: root 18 | 19 | labelText: qsTr("Chat address") 20 | placeholderText: qsTr("user@example.org") 21 | inputMethodHints: Qt.ImhEmailCharactersOnly | Qt.ImhPreferLowercase | TextFieldCompleter.inputMethodHints 22 | invalidHintText: qsTr("The chat address must have the form username@server") 23 | valid: credentialsValidator.isUserJidValid(text) 24 | text: AccountController.account.jid 25 | completionRole: "display" 26 | completionModel: HostCompletionProxyModel { 27 | userInput: root.input 28 | sourceModel: HostCompletionModel 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/qml/elements/fields/PasswordField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 2 | // SPDX-FileCopyrightText: 2023 Linus Jahn 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import org.kde.kirigami as Kirigami 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * This is a password field with a hint for empty passwords and an option for showing the password. 13 | */ 14 | CredentialsField { 15 | // indicator for showing the hidden password 16 | property bool showPassword: false 17 | 18 | labelText: qsTr("Password") 19 | 20 | inputField { 21 | echoMode: showPassword ? TextInput.Normal : TextInput.Password 22 | rightActions: [ 23 | Kirigami.Action { 24 | icon.name: showPassword ? "password-show-on" : "password-show-off" 25 | onTriggered: showPassword = !showPassword 26 | } 27 | ] 28 | } 29 | invalidHintText: qsTr("Please enter a valid password") 30 | valid: credentialsValidator.isPasswordValid(text) 31 | } 32 | -------------------------------------------------------------------------------- /src/qml/elements/fields/RegistrationPasswordField.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | 9 | import im.kaidan.kaidan 10 | 11 | /** 12 | * This is a password field for registration with an option for showing the password and extras regarding registration. 13 | * The password strength is indicated by a colored bar. 14 | * A hint is shown for improving the password strength. 15 | */ 16 | PasswordField { 17 | readonly property string generatedPassword: credentialsGenerator.generatePassword() 18 | 19 | placeholderText: { 20 | if (inputField.echoMode === TextInput.Password) { 21 | return "●".repeat(generatedPassword.length) 22 | } 23 | 24 | return generatedPassword 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/qml/qml.qrc.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Linus Jahn 2 | 3 | SPDX-License-Identifier: CC0-1.0 4 | -------------------------------------------------------------------------------- /src/qml/registration/CustomDataFormArea.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import QtQuick.Layouts 8 | import org.kde.kirigami as Kirigami 9 | import org.kde.kirigamiaddons.formcard as FormCard 10 | 11 | import im.kaidan.kaidan 12 | 13 | import "../elements" 14 | 15 | /** 16 | * This a form for custom data requested by a server for registration. 17 | */ 18 | FormCard.FormCard { 19 | id: root 20 | 21 | property alias model: form.model 22 | property alias lastTextFieldAcceptedFunction: form.lastTextFieldAcceptedFunction 23 | 24 | FormCard.FormHeader { 25 | title: qsTr("Enter additional information") 26 | } 27 | 28 | FormCard.FormTextDelegate { 29 | text: qsTr("The provider has requested more information") 30 | description: qsTr("Not everything may be required") 31 | } 32 | 33 | Kirigami.Separator { 34 | Layout.fillWidth: true 35 | } 36 | 37 | FormCard.AbstractFormDelegate { 38 | background: NonInteractiveFormDelegateBackground {} 39 | contentItem: ColumnLayout { 40 | DataForm { 41 | id: form 42 | displayTitle: false 43 | displayInstructions: false 44 | Layout.fillWidth: true 45 | } 46 | } 47 | } 48 | 49 | function forceActiveFocus() { 50 | form.nextItemInFocusChain().forceActiveFocus() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/qml/registration/RegistrationButton.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 Linus Jahn 2 | // SPDX-FileCopyrightText: 2020 Melvin Keskin 3 | // 4 | // SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | import QtQuick 7 | import org.kde.kirigami as Kirigami 8 | import org.kde.kirigamiaddons.formcard as FormCard 9 | 10 | import im.kaidan.kaidan 11 | 12 | import "../elements" 13 | 14 | /** 15 | * This is an area containing a button used for confirming a registration. 16 | */ 17 | FormCard.FormCard { 18 | property var registrationFunction 19 | property var loginFunction 20 | property alias busy: button.busy 21 | 22 | BusyIndicatorFormButton { 23 | id: button 24 | idleText: Kaidan.connectionError === ClientWorker.EmailConfirmationRequired ? qsTr("Log in after email confirmation") : qsTr("Register") 25 | busyText: Kaidan.connectionError === ClientWorker.EmailConfirmationRequired ? qsTr("Logging in…") : qsTr("Registering…") 26 | background: HighlightedFormButtonBackground {} 27 | onClicked: { 28 | if (Kaidan.connectionError === ClientWorker.EmailConfirmationRequired) { 29 | loginFunction() 30 | } else { 31 | registrationFunction() 32 | } 33 | 34 | busy = true 35 | } 36 | 37 | Connections { 38 | target: Kaidan.registrationController 39 | 40 | function onRegistrationFailed(error, errorMessage) { 41 | button.busy = false 42 | } 43 | 44 | function onConnectionErrorChanged(error) { 45 | button.busy = false 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/qml/registration/RegistrationRequestPage.qml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Melvin Keskin 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | import QtQuick 6 | 7 | import im.kaidan.kaidan 8 | 9 | import ".." 10 | 11 | /** 12 | * This is the base for pages requesting to register an account. 13 | */ 14 | ImageBackgroundPage { 15 | // JID of the provider from whom the registration form is requested 16 | property string provider 17 | 18 | onBackRequested: Kaidan.registrationController.abortRegistration() 19 | Component.onCompleted: AccountController.resetCustomConnectionSettings() 20 | 21 | /** 22 | * Requests a registration form from the provider. 23 | */ 24 | function requestRegistrationForm() { 25 | // Set the provider's JID. 26 | AccountController.setNewAccountJid(provider) 27 | 28 | // Request a registration form. 29 | Kaidan.registrationController.requestRegistrationForm() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/static_plugins.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2018 Ilya Bizyaev 2 | // SPDX-FileCopyrightText: 2020 Jonah Brüchert 3 | // SPDX-FileCopyrightText: 2020 Linus Jahn 4 | // 5 | // SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | #pragma once 8 | 9 | // This file imports static plugin classes for static plugins 10 | #include 11 | 12 | #define QT_STATICPLUGIN 13 | 14 | #ifdef Q_OS_IOS 15 | Q_IMPORT_PLUGIN(QIOSIntegrationPlugin) 16 | Q_IMPORT_PLUGIN(QMacHeifPlugin) 17 | Q_IMPORT_PLUGIN(QMacJp2Plugin) 18 | Q_IMPORT_PLUGIN(QICNSPlugin) 19 | Q_IMPORT_PLUGIN(QTgaPlugin) 20 | Q_IMPORT_PLUGIN(QTiffPlugin) 21 | Q_IMPORT_PLUGIN(QWbmpPlugin) 22 | Q_IMPORT_PLUGIN(QWebpPlugin) 23 | #endif // Q_OS_IOS 24 | 25 | #ifdef Q_OS_WIN 26 | Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); 27 | #endif 28 | 29 | // Media support 30 | Q_IMPORT_PLUGIN(QSvgPlugin) 31 | Q_IMPORT_PLUGIN(QSvgIconPlugin) 32 | Q_IMPORT_PLUGIN(QGifPlugin) 33 | Q_IMPORT_PLUGIN(QICOPlugin) 34 | Q_IMPORT_PLUGIN(QJpegPlugin) 35 | 36 | // Qt Quick and network 37 | Q_IMPORT_PLUGIN(QLocalClientConnectionFactory) 38 | Q_IMPORT_PLUGIN(QTcpServerConnectionFactory) 39 | Q_IMPORT_PLUGIN(QGenericEnginePlugin) 40 | Q_IMPORT_PLUGIN(QSQLiteDriverPlugin) 41 | Q_IMPORT_PLUGIN(KirigamiPlugin) 42 | 43 | Q_IMPORT_PLUGIN(QtQuick2Plugin) 44 | Q_IMPORT_PLUGIN(QtLabsPlatformPlugin) 45 | Q_IMPORT_PLUGIN(QtQuickControls2Plugin) 46 | Q_IMPORT_PLUGIN(QtQuickControls2MaterialStylePlugin) 47 | Q_IMPORT_PLUGIN(QtQuickControls2UniversalStylePlugin) 48 | Q_IMPORT_PLUGIN(QtQuickLayoutsPlugin) 49 | Q_IMPORT_PLUGIN(QtQuick2WindowPlugin) 50 | Q_IMPORT_PLUGIN(QmlShapesPlugin) 51 | Q_IMPORT_PLUGIN(QtQuickTemplates2Plugin) 52 | Q_IMPORT_PLUGIN(QtQmlModelsPlugin) 53 | -------------------------------------------------------------------------------- /tests/FutureUtilsTest.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Linus Jahn 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | // Qt 6 | #include 7 | // Kaidan 8 | #include "FutureUtils.h" 9 | #include "Test.h" 10 | 11 | class FutureUtilsTest : public Test 12 | { 13 | Q_OBJECT 14 | 15 | private: 16 | Q_SLOT void testJoin(); 17 | }; 18 | 19 | void FutureUtilsTest::testJoin() 20 | { 21 | QFutureInterface i1; 22 | QFutureInterface i2; 23 | QFutureInterface i3; 24 | 25 | auto futures = QList{i1.future(), i2.future(), i3.future()}; 26 | auto joinedFuture = join(this, futures); 27 | 28 | i2.reportResult(10); 29 | i2.reportFinished(); 30 | i3.reportResult(11); 31 | i3.reportFinished(); 32 | i1.reportResult(9); 33 | i1.reportFinished(); 34 | 35 | while (!joinedFuture.isFinished()) { 36 | QCoreApplication::processEvents(); 37 | } 38 | 39 | QVERIFY(joinedFuture.isFinished()); 40 | QCOMPARE(joinedFuture.result(), (QList{9, 10, 11})); 41 | } 42 | 43 | QTEST_GUILESS_MAIN(FutureUtilsTest) 44 | #include "FutureUtilsTest.moc" 45 | -------------------------------------------------------------------------------- /tests/Test.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #include "Test.h" 6 | 7 | // Qt 8 | #include 9 | #include 10 | 11 | void Test::initTestCase() 12 | { 13 | QStandardPaths::setTestModeEnabled(true); 14 | 15 | // Remove old test data. 16 | removeTestDataDirectory(); 17 | } 18 | 19 | void Test::removeTestDataDirectory() 20 | { 21 | const auto testDirectoryPath = QStandardPaths::writableLocation(QStandardPaths::StandardLocation::AppDataLocation); 22 | 23 | if (auto testDirectory = QDir(testDirectoryPath); testDirectory.exists()) { 24 | QVERIFY(testDirectory.removeRecursively()); 25 | } 26 | } 27 | 28 | #include "moc_Test.cpp" 29 | -------------------------------------------------------------------------------- /tests/Test.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2025 Filipe Azevedo 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later 4 | 5 | #pragma once 6 | 7 | // Qt 8 | #include 9 | 10 | class Test : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | using QObject::QObject; 16 | 17 | protected Q_SLOTS: 18 | virtual void initTestCase(); 19 | 20 | private: 21 | void removeTestDataDirectory(); 22 | }; 23 | -------------------------------------------------------------------------------- /tests/TestUtils.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2022 Linus Jahn 2 | // 3 | // SPDX-License-Identifier: GPL-3.0-or-later OR LGPL-2.1-or-later 4 | 5 | #pragma once 6 | 7 | // std 8 | #include 9 | // Qt 10 | #include 11 | #include 12 | #include 13 | #include 14 | // QXmpp 15 | #include 16 | 17 | template 18 | T wait(const QFuture &future) 19 | { 20 | auto watcher = std::make_unique>(); 21 | QSignalSpy spy(watcher.get(), &QFutureWatcherBase::finished); 22 | watcher->setFuture(future); 23 | spy.wait(); 24 | if constexpr (!std::is_same_v) { 25 | return future.result(); 26 | } 27 | } 28 | 29 | template 30 | T wait(QObject *context, QXmppTask task) 31 | { 32 | return wait(task.toFuture(context)); 33 | } 34 | -------------------------------------------------------------------------------- /utils/code-tidy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # SPDX-FileCopyrightText: 2018 Linus Jahn 3 | # 4 | # SPDX-License-Identifier: GPL-3.0-or-later 5 | 6 | # Tidy up code to use modern C++ 7 | 8 | KAIDAN_SOURCES=$(dirname "$(readlink -f "${0}")")/.. 9 | 10 | mkdir -p .tidy-tmp 11 | cd .tidy-tmp 12 | 13 | cmake $KAIDAN_SOURCES -DCMAKE_EXPORT_COMPILE_COMMANDS=ON 14 | 15 | # will output an error about moc files: just ignore, we don't want to tidy moc generated files 16 | run-clang-tidy -header-filter='.*' -checks='-*,modernize-avoid-bind,modernize-deprecated-headers,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-shrink-to-fit,modernize-use-auto,modernize-use-bool-literals,modernize-use-default,modernize-use-emplace,modernize-use-nullptr,modernize-use-using' -fix 17 | 18 | git -C $KAIDAN_SOURCES checkout -- src/singleapp 19 | 20 | cd .. 21 | rm -rf .tidy-tmp 22 | 23 | -------------------------------------------------------------------------------- /utils/optimize-png.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2023 Melvin Keskin 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | if [ "$1" = "--help" ] || [ "$1" = "" ] 8 | then 9 | echo "Usage: $0 " 10 | echo "Example: $0 image.png" 11 | exit 12 | fi 13 | 14 | png_file="$1" 15 | 16 | echo "*****************************************" 17 | echo "Optimizing ${png_file}" 18 | echo "*****************************************" 19 | 20 | optipng -o7 -quiet "${png_file}" >/dev/null && \ 21 | advpng -z4 "${png_file}" >/dev/null 22 | -------------------------------------------------------------------------------- /utils/render-logos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-FileCopyrightText: 2019 Ilya Bizyaev 3 | # SPDX-FileCopyrightText: 2020 Linus Jahn 4 | # SPDX-FileCopyrightText: 2020 Jonah Brüchert 5 | # SPDX-FileCopyrightText: 2023 Melvin Keskin 6 | # 7 | # SPDX-License-Identifier: GPL-3.0-or-later 8 | 9 | KAIDAN_SOURCES=$(dirname "$(readlink -f "${0}")")/.. 10 | OPTIMIZE_PNG_SCRIPT="${KAIDAN_SOURCES}/utils/optimize-png.sh" 11 | 12 | echo "*****************************************" 13 | echo "Rendering logos" 14 | echo "*****************************************" 15 | 16 | rendersvg() { 17 | inkscape -o $2 -w $3 -h $3 $1 >/dev/null 18 | "${OPTIMIZE_PNG_SCRIPT}" "${2}" 19 | } 20 | 21 | androidlogo() { 22 | echo "Rendering $KAIDAN_SOURCES/misc/android/res/mipmap-$1..." 23 | mkdir -p $KAIDAN_SOURCES/misc/android/res/mipmap-$1 24 | rendersvg $KAIDAN_SOURCES/data/images/kaidan.svg "$KAIDAN_SOURCES/misc/android/res/mipmap-$1/icon.png" $2 25 | rendersvg $KAIDAN_SOURCES/data/images/kaidan.svg "$KAIDAN_SOURCES/misc/android/res/mipmap-$1/logo.png" $(( $2 * 4 )) 26 | } 27 | 28 | # App icons for ECM 29 | app_icon() { 30 | rendersvg $KAIDAN_SOURCES/data/images/kaidan.svg "$KAIDAN_SOURCES/misc/app-icons/$1-kaidan.png" $1 31 | optipng -o9 "$KAIDAN_SOURCES/misc/app-icons/$1-kaidan.png" 32 | } 33 | 34 | mkdir -p $KAIDAN_SOURCES/misc/windows 35 | 36 | app_icon 16 37 | app_icon 32 38 | app_icon 48 39 | app_icon 256 40 | 41 | androidlogo ldpi 36 42 | androidlogo mdpi 48 43 | androidlogo hdpi 72 44 | androidlogo xhdpi 96 45 | androidlogo xxhdpi 144 46 | androidlogo xxxhdpi 192 47 | -------------------------------------------------------------------------------- /utils/update-providers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # SPDX-FileCopyrightText: 2021 Melvin Keskin 3 | # SPDX-FileCopyrightText: 2023 Linus Jahn 4 | # SPDX-FileCopyrightText: 2023 Filipe Azevedo 5 | # 6 | # SPDX-License-Identifier: GPL-3.0-or-later 7 | 8 | # 9 | # This script updates the provider lists used for registration and autocomplete. 10 | # 11 | # It should be run before each new release. 12 | # It prints the modified files. 13 | # 14 | 15 | XMPP_PROVIDERS_VERSION=2 16 | XMPP_PROVIDERS_ROOT_URL="https://data.xmpp.net/providers/v${XMPP_PROVIDERS_VERSION}" 17 | KAIDAN_ROOT_DIRECTORY=$(dirname "$(readlink -f "${0}")")/.. 18 | TARGET_DIRECTORY="${KAIDAN_ROOT_DIRECTORY}/data" 19 | PROVIDER_FILE_NAMES=( 20 | "providers-B providers" 21 | "providers-Ds providers-completion" 22 | ) 23 | 24 | for provider_file_name in "${PROVIDER_FILE_NAMES[@]}" 25 | do 26 | source_provider_file_name=$(echo "$provider_file_name" | awk '{print $1}') 27 | source_provider_file_url="${XMPP_PROVIDERS_ROOT_URL}/${source_provider_file_name}.json" 28 | 29 | target_provider_file_name=$(echo "$provider_file_name" | awk '{print $2}') 30 | target_provider_file_path="${TARGET_DIRECTORY}/${target_provider_file_name}.json" 31 | 32 | curl -s -L "${source_provider_file_url}" > "${target_provider_file_path}" 33 | git diff --name-only "${target_provider_file_path}" 34 | done 35 | -------------------------------------------------------------------------------- /utils/update-submodule-breeze-icons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # SPDX-FileCopyrightText: 2023 Melvin Keskin 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | # 8 | # This script updates the Git submodule for Breeze Icons. 9 | # 10 | # It should be run before each new release. 11 | # 12 | 13 | KAIDAN_DIRECTORY=$(dirname "$(readlink -f "${0}")")/.. 14 | BREEZE_ICONS_DIRECTORY="${KAIDAN_DIRECTORY}/3rdparty/breeze-icons" 15 | 16 | git submodule update --init --recursive 17 | cd "${BREEZE_ICONS_DIRECTORY}" 18 | git checkout master 19 | cd "${KAIDAN_DIRECTORY}" 20 | git add "${BREEZE_ICONS_DIRECTORY}" 21 | git commit -m "Update Git submodule breeze-icons" 22 | --------------------------------------------------------------------------------