mirror of
https://gitlab.gnome.org/World/fractal.git
synced 2025-08-14 00:02:20 -04:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e3c34328ee | ||
|
0e9d34dd9d | ||
|
4597519128 | ||
|
8a7c690d21 | ||
|
07cc8f9787 | ||
|
2a09a76fb0 | ||
|
ddc5001a79 | ||
|
ae53630df3 | ||
|
6f090f3883 | ||
|
b343862f35 | ||
|
c7c9d5d974 | ||
|
10ed8358f9 | ||
|
0be7615056 | ||
|
a0955e225a | ||
|
2ae6f75938 | ||
|
420ec4d24e | ||
|
7bd9e7fa45 | ||
|
e9f6873d8a | ||
|
9dcee3a6ac |
472
Cargo.lock
generated
472
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fractal"
|
name = "fractal"
|
||||||
version = "12.0.0-rc"
|
version = "12.0.0"
|
||||||
authors = ["Julian Sparber <julian@sparber.net>"]
|
authors = ["Julian Sparber <julian@sparber.net>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.85"
|
rust-version = "1.85"
|
||||||
@ -61,7 +61,7 @@ zeroize = "1"
|
|||||||
|
|
||||||
# gtk-rs project and dependents. These usually need to be updated together.
|
# gtk-rs project and dependents. These usually need to be updated together.
|
||||||
adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] }
|
adw = { package = "libadwaita", version = "0.7", features = ["v1_7"] }
|
||||||
glycin = { version = "2", default-features = false, features = ["tokio", "gdk4"] }
|
glycin = { version = "3.0.0-beta.1", default-features = false, features = ["tokio", "gdk4"] }
|
||||||
gst = { version = "0.23", package = "gstreamer" }
|
gst = { version = "0.23", package = "gstreamer" }
|
||||||
gst_app = { version = "0.23", package = "gstreamer-app" }
|
gst_app = { version = "0.23", package = "gstreamer-app" }
|
||||||
gst_pbutils = { version = "0.23", package = "gstreamer-pbutils" }
|
gst_pbutils = { version = "0.23", package = "gstreamer-pbutils" }
|
||||||
@ -74,23 +74,23 @@ sourceview = { package = "sourceview5", version = "0.9" }
|
|||||||
[dependencies.matrix-sdk]
|
[dependencies.matrix-sdk]
|
||||||
# version = "0.13"
|
# version = "0.13"
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||||
rev = "ada68e11144507afc9d178f4264452aae1ff9e27"
|
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||||
features = ["socks", "sso-login", "markdown", "qrcode"]
|
features = ["socks", "sso-login", "markdown", "qrcode"]
|
||||||
|
|
||||||
[dependencies.matrix-sdk-store-encryption]
|
[dependencies.matrix-sdk-store-encryption]
|
||||||
# version = "0.13"
|
# version = "0.13"
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||||
rev = "ada68e11144507afc9d178f4264452aae1ff9e27"
|
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||||
|
|
||||||
[dependencies.matrix-sdk-ui]
|
[dependencies.matrix-sdk-ui]
|
||||||
# version = "0.13"
|
# version = "0.13"
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
git = "https://github.com/matrix-org/matrix-rust-sdk.git"
|
||||||
rev = "ada68e11144507afc9d178f4264452aae1ff9e27"
|
rev = "a9ce1c6e5822b8eb8411c5bc257049d9a9d15884"
|
||||||
|
|
||||||
[dependencies.ruma]
|
[dependencies.ruma]
|
||||||
# version = "0.12.5"
|
# version = "0.12.5"
|
||||||
git = "https://github.com/ruma/ruma.git"
|
git = "https://github.com/ruma/ruma.git"
|
||||||
rev = "de19ebaf71af620eb17abaefd92e43153f9d041d"
|
rev = "a2fe858133ba932b4bda730dc7472c9c985739a0"
|
||||||
features = [
|
features = [
|
||||||
"client-api-c",
|
"client-api-c",
|
||||||
"markdown",
|
"markdown",
|
||||||
|
@ -38,7 +38,7 @@ development version while keeping the stable release around for daily use.
|
|||||||
|
|
||||||
### Stable version
|
### Stable version
|
||||||
|
|
||||||
The current stable version is 11.2 (released June 10th 2025).
|
The current stable version is 12 (released August 11th 2025).
|
||||||
|
|
||||||
You can get the official Fractal Flatpak from Flathub.
|
You can get the official Fractal Flatpak from Flathub.
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ You can get the official Fractal Flatpak from Flathub.
|
|||||||
|
|
||||||
### Beta version
|
### Beta version
|
||||||
|
|
||||||
The current beta version is 12.rc (released July 31st 2025).
|
The current beta version is 12 (same as stable).
|
||||||
|
|
||||||
It is available as a Flatpak on Flathub Beta.
|
It is available as a Flatpak on Flathub Beta.
|
||||||
|
|
||||||
|
@ -95,27 +95,6 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "glycin-loaders",
|
|
||||||
"buildsystem": "meson",
|
|
||||||
"config-opts": [
|
|
||||||
"-Dtests=false",
|
|
||||||
"-Dlibglycin=false",
|
|
||||||
"-Dintrospection=false",
|
|
||||||
"-Dvapi=false",
|
|
||||||
"-Dcapi_docs=false",
|
|
||||||
"-Dpython_tests=false"
|
|
||||||
],
|
|
||||||
"sources": [
|
|
||||||
{
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://gitlab.gnome.org/sophie-h/glycin.git",
|
|
||||||
"tag": "1.2.2",
|
|
||||||
"commit": "c7d362287303944721cf583d4d9e9f7721bfa407",
|
|
||||||
"disable-submodules": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "fractal",
|
"name": "fractal",
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
|
@ -35,15 +35,15 @@
|
|||||||
|
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-11/screenshots/main.png</image>
|
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/main.png</image>
|
||||||
<caption>Fractal’s main window</caption>
|
<caption>Fractal’s main window</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-11/screenshots/media-history.png</image>
|
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/media-history.png</image>
|
||||||
<caption>View the media history of a Matrix room</caption>
|
<caption>View the media history of a Matrix room</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-11/screenshots/adaptive.png</image>
|
<image type="source">https://gitlab.gnome.org/World/fractal/raw/fractal-12/screenshots/adaptive.png</image>
|
||||||
<caption>Fractal’s interface adapts to small screens</caption>
|
<caption>Fractal’s interface adapts to small screens</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
@ -71,66 +71,44 @@
|
|||||||
</content_rating>
|
</content_rating>
|
||||||
|
|
||||||
<releases>@development-release@
|
<releases>@development-release@
|
||||||
<release version="12~rc" type="development" date="2025-07-31">
|
<release version="12" type="stable" date="2025-08-11">
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
Want to get a head start and try out Fractal 12 before its release? That’s what this
|
Knock, knock, knock… on rooms, baby 🎵 Ooh ooh ooh ooh ooh ooh 🎶 That's right, Fractal 12
|
||||||
Release Candidate is for! New since 12.beta:
|
adds support for knocking, among other things. Read all about the improvements since 11.2:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
The upcoming room version 12 is supported, with the special power level of room creators
|
Requesting invites to rooms (aka knocking) is now possible, as is enabling such requests
|
||||||
|
for room admins.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Requesting invites to rooms (aka knocking) is now possible
|
The upcoming room version 12 is supported, with the special power level of room
|
||||||
</li>
|
creators.
|
||||||
<li>
|
|
||||||
Clicking on the name of the sender of a message adds a mention to them in the composer
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<p>
|
|
||||||
As usual, this release includes other improvements, fixes and new translations thanks to
|
|
||||||
all our contributors, and our upstream projects.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
As the version implies, it should be mostly stable and we expect to only include minor
|
|
||||||
improvements until the release of Fractal 12.
|
|
||||||
</p>
|
|
||||||
</description>
|
|
||||||
</release>
|
|
||||||
<release version="12~beta" type="development" date="2025-06-26">
|
|
||||||
<description>
|
|
||||||
<p>
|
|
||||||
Hot! Hot! Hot! No, we are not talking about the summer weather in the northern hemisphere,
|
|
||||||
but about the brand new release of Fractal 12.beta! Coming soon to your device:
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
The safety setting to hide media previews in rooms is now synced between Matrix
|
|
||||||
clients.
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
We added another safety setting (which is also synced) to hide avatars in invites.
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
A room can be marked as unread via the context menu in the sidebar.
|
A room can be marked as unread via the context menu in the sidebar.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
|
||||||
We changed the UX a little for tombstoned rooms. Instead of showing a banner at the top
|
|
||||||
of the history, it now replaces the composer at the bottom of the history.
|
|
||||||
</li>
|
|
||||||
<li>
|
<li>
|
||||||
You can now see if a section in the sidebar has any notifications or activity when it is
|
You can now see if a section in the sidebar has any notifications or activity when it is
|
||||||
collapsed.
|
collapsed.
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
Clicking on the name of the sender of a message adds a mention to them in the composer.
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
The safety setting to hide media previews in rooms is now synced between Matrix clients
|
||||||
|
and we added another safety setting (which is also synced) to hide avatars in invites.
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
As usual, this release includes other improvements, fixes and new translations thanks to
|
As usual, this release includes other improvements, fixes and new translations thanks to
|
||||||
all our contributors, and our upstream projects.
|
all our contributors, and our upstream projects.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
As the version implies, there might be a slight risk of regressions, but it should be
|
We want to address special thanks to the translators who worked on this version. We know
|
||||||
mostly stable. If all goes well the next step is the release candidate!
|
this is a huge undertaking and have a deep appreciation for what you’ve done. If you want
|
||||||
|
to help with this effort, head over to Damned Lies.
|
||||||
</p>
|
</p>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
|
@ -120,8 +120,10 @@ sidebar {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
min-width: 0.7em;
|
min-width: 0.8em;
|
||||||
padding: 2px 5px;
|
min-height: 0.8em;
|
||||||
|
line-height: 0.8em;
|
||||||
|
padding: 0.4em 5px;
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
background-color: color-mix(in srgb, currentColor 15%, transparent);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
project('fractal',
|
project('fractal',
|
||||||
'rust',
|
'rust',
|
||||||
version: '12.rc',
|
version: '12',
|
||||||
license: 'GPL-3.0-or-later',
|
license: 'GPL-3.0-or-later',
|
||||||
meson_version: '>= 1.1')
|
meson_version: '>= 1.1')
|
||||||
|
|
||||||
@ -11,7 +11,7 @@ base_id = 'org.gnome.Fractal'
|
|||||||
application_id = base_id
|
application_id = base_id
|
||||||
|
|
||||||
major_version = '12'
|
major_version = '12'
|
||||||
pre_release_version = 'rc'
|
pre_release_version = ''
|
||||||
|
|
||||||
version = major_version
|
version = major_version
|
||||||
if pre_release_version != ''
|
if pre_release_version != ''
|
||||||
|
1198
po/pt_BR.po
1198
po/pt_BR.po
File diff suppressed because it is too large
Load Diff
123
po/sl.po
123
po/sl.po
@ -9,8 +9,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: fractal master\n"
|
"Project-Id-Version: fractal master\n"
|
||||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
||||||
"POT-Creation-Date: 2025-07-29 01:25+0000\n"
|
"POT-Creation-Date: 2025-07-31 20:08+0000\n"
|
||||||
"PO-Revision-Date: 2025-07-29 10:53+0200\n"
|
"PO-Revision-Date: 2025-08-01 12:38+0200\n"
|
||||||
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
"Last-Translator: Martin Srebotnjak <miles@filmsi.net>\n"
|
||||||
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
|
"Language-Team: Slovenian GNOME Translation Team <gnome-si@googlegroups.com>\n"
|
||||||
"Language: sl_SI\n"
|
"Language: sl_SI\n"
|
||||||
@ -594,8 +594,8 @@ msgstr "Zapusti"
|
|||||||
#: src/identity_verification_view/confirm_qr_code_page.ui:52
|
#: src/identity_verification_view/confirm_qr_code_page.ui:52
|
||||||
#: src/session/view/account_settings/general_page/mod.rs:391
|
#: src/session/view/account_settings/general_page/mod.rs:391
|
||||||
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
||||||
#: src/session/view/content/room_details/general_page.rs:1033
|
#: src/session/view/content/room_details/general_page.rs:1036
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:116
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:112
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:583
|
#: src/session/view/content/room_history/event_actions/group.rs:583
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:647
|
#: src/session/view/content/room_history/event_actions/group.rs:647
|
||||||
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
||||||
@ -1869,7 +1869,7 @@ msgstr "Člani {room}"
|
|||||||
msgid "Any registered user"
|
msgid "Any registered user"
|
||||||
msgstr "Vsak registriran uporabnik"
|
msgstr "Vsak registriran uporabnik"
|
||||||
|
|
||||||
#: src/session/model/room/join_rule.rs:288 src/session/view/content/room_details/general_page.rs:939
|
#: src/session/model/room/join_rule.rs:288 src/session/view/content/room_details/general_page.rs:942
|
||||||
msgid "Unsupported rule"
|
msgid "Unsupported rule"
|
||||||
msgstr "Nepodprto pravilo"
|
msgstr "Nepodprto pravilo"
|
||||||
|
|
||||||
@ -2903,7 +2903,7 @@ msgid "Remove “{address}”"
|
|||||||
msgstr "Odstrani »{address}«"
|
msgstr "Odstrani »{address}«"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/addresses_subpage/mod.rs:431
|
#: src/session/view/content/room_details/addresses_subpage/mod.rs:431
|
||||||
#: src/session/view/content/room_details/general_page.rs:677 src/session/view/create_room_dialog.ui:132
|
#: src/session/view/content/room_details/general_page.rs:680 src/session/view/create_room_dialog.ui:132
|
||||||
msgid "Main Address"
|
msgid "Main Address"
|
||||||
msgstr "Glavni naslov"
|
msgstr "Glavni naslov"
|
||||||
|
|
||||||
@ -3048,8 +3048,8 @@ msgstr "Opis"
|
|||||||
msgid "Save Description"
|
msgid "Save Description"
|
||||||
msgstr "Shrani opis"
|
msgstr "Shrani opis"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:462
|
#: src/session/view/content/room_details/general_page.rs:465
|
||||||
#: src/session/view/content/room_details/general_page.rs:508
|
#: src/session/view/content/room_details/general_page.rs:511
|
||||||
#: src/session/view/content/room_details/invite_subpage/list.rs:260
|
#: src/session/view/content/room_details/invite_subpage/list.rs:260
|
||||||
msgid "Member"
|
msgid "Member"
|
||||||
msgid_plural "Members"
|
msgid_plural "Members"
|
||||||
@ -3058,71 +3058,71 @@ msgstr[1] "člani"
|
|||||||
msgstr[2] "člani"
|
msgstr[2] "člani"
|
||||||
msgstr[3] "člani"
|
msgstr[3] "člani"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:632
|
#: src/session/view/content/room_details/general_page.rs:635
|
||||||
msgid "Could not change notifications setting"
|
msgid "Could not change notifications setting"
|
||||||
msgstr "Nastavitve obvestil ni bilo mogoče spremeniti"
|
msgstr "Nastavitve obvestil ni bilo mogoče spremeniti"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:672
|
#: src/session/view/content/room_details/general_page.rs:675
|
||||||
#: src/session/view/content/room_details/general_page.rs:711
|
#: src/session/view/content/room_details/general_page.rs:714
|
||||||
msgid "Copy address"
|
msgid "Copy address"
|
||||||
msgstr "Kopiraj naslov"
|
msgstr "Kopiraj naslov"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:673
|
#: src/session/view/content/room_details/general_page.rs:676
|
||||||
#: src/session/view/content/room_details/general_page.rs:712
|
#: src/session/view/content/room_details/general_page.rs:715
|
||||||
msgid "Address copied to clipboard"
|
msgid "Address copied to clipboard"
|
||||||
msgstr "Naslov je kopiran v odložišče"
|
msgstr "Naslov je kopiran v odložišče"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:754
|
#: src/session/view/content/room_details/general_page.rs:757
|
||||||
msgid "Room link copied to clipboard"
|
msgid "Room link copied to clipboard"
|
||||||
msgstr "Povezava do klepetalnice kopirana v odložišče"
|
msgstr "Povezava do klepetalnice kopirana v odložišče"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:815
|
#: src/session/view/content/room_details/general_page.rs:818
|
||||||
msgid "Could not change guest access"
|
msgid "Could not change guest access"
|
||||||
msgstr "Ni bilo mogoče spremeniti dostopa za goste"
|
msgstr "Ni bilo mogoče spremeniti dostopa za goste"
|
||||||
|
|
||||||
#. Translators: Do NOT translate the content between '{' and '}',
|
#. Translators: Do NOT translate the content between '{' and '}',
|
||||||
#. this is a variable name.
|
#. this is a variable name.
|
||||||
#: src/session/view/content/room_details/general_page.rs:832
|
#: src/session/view/content/room_details/general_page.rs:835
|
||||||
msgid "Publish in the {homeserver} directory"
|
msgid "Publish in the {homeserver} directory"
|
||||||
msgstr "Objavi v imeniku {homeserver}"
|
msgstr "Objavi v imeniku {homeserver}"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:907
|
#: src/session/view/content/room_details/general_page.rs:910
|
||||||
msgid "Could not publish room in directory"
|
msgid "Could not publish room in directory"
|
||||||
msgstr "Klepetalnice ni bilo mogoče objaviti v imeniku"
|
msgstr "Klepetalnice ni bilo mogoče objaviti v imeniku"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:909
|
#: src/session/view/content/room_details/general_page.rs:912
|
||||||
msgid "Could not unpublish room from directory"
|
msgid "Could not unpublish room from directory"
|
||||||
msgstr "Objave klepetalnice v imeniku ni bilo mogoče preklicati"
|
msgstr "Objave klepetalnice v imeniku ni bilo mogoče preklicati"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:930
|
#: src/session/view/content/room_details/general_page.rs:933
|
||||||
#: src/session/view/content/room_details/general_page.ui:274
|
#: src/session/view/content/room_details/general_page.ui:274
|
||||||
msgid "Anyone, even if they are not in the room"
|
msgid "Anyone, even if they are not in the room"
|
||||||
msgstr "Kdorkoli, tudi če ni v klepetalnici"
|
msgstr "Kdorkoli, tudi če ni v klepetalnici"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:933
|
#: src/session/view/content/room_details/general_page.rs:936
|
||||||
#: src/session/view/content/room_details/general_page.ui:275
|
#: src/session/view/content/room_details/general_page.ui:275
|
||||||
msgid "Members only, since this option was selected"
|
msgid "Members only, since this option was selected"
|
||||||
msgstr "Samo člani, saj je bila ta možnost izbrana"
|
msgstr "Samo člani, saj je bila ta možnost izbrana"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:935
|
#: src/session/view/content/room_details/general_page.rs:938
|
||||||
#: src/session/view/content/room_details/general_page.ui:277
|
#: src/session/view/content/room_details/general_page.ui:277
|
||||||
msgid "Members only, since they were invited"
|
msgid "Members only, since they were invited"
|
||||||
msgstr "Samo člani, saj so bili povabljeni"
|
msgstr "Samo člani, saj so bili povabljeni"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:937
|
#: src/session/view/content/room_details/general_page.rs:940
|
||||||
#: src/session/view/content/room_details/general_page.ui:276
|
#: src/session/view/content/room_details/general_page.ui:276
|
||||||
msgid "Members only, since they joined the room"
|
msgid "Members only, since they joined the room"
|
||||||
msgstr "Samo člani, ker so se pridružili klepetalnici"
|
msgstr "Samo člani, ker so se pridružili klepetalnici"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:986
|
#: src/session/view/content/room_details/general_page.rs:989
|
||||||
msgid "Could not change who can read history"
|
msgid "Could not change who can read history"
|
||||||
msgstr "Ni bilo mogoče spremeniti, kdo sme brati zgodovino"
|
msgstr "Ni bilo mogoče spremeniti, kdo sme brati zgodovino"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1028
|
#: src/session/view/content/room_details/general_page.rs:1031
|
||||||
msgid "Enable Encryption?"
|
msgid "Enable Encryption?"
|
||||||
msgstr "Želite omogočiti šifriranja?"
|
msgstr "Želite omogočiti šifriranja?"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1029
|
#: src/session/view/content/room_details/general_page.rs:1032
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enabling encryption will prevent new members to read the history before they arrived. This cannot be "
|
"Enabling encryption will prevent new members to read the history before they arrived. This cannot be "
|
||||||
"disabled later."
|
"disabled later."
|
||||||
@ -3130,30 +3130,30 @@ msgstr ""
|
|||||||
"Če omogočite šifriranje, novi člani ne bodo mogli prebrati zgodovine pred svojo včlanitvijo. Tega "
|
"Če omogočite šifriranje, novi člani ne bodo mogli prebrati zgodovine pred svojo včlanitvijo. Tega "
|
||||||
"pozneje ni možno onemogočiti."
|
"pozneje ni možno onemogočiti."
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1034 src/session/view/sidebar/mod.rs:320
|
#: src/session/view/content/room_details/general_page.rs:1037 src/session/view/sidebar/mod.rs:320
|
||||||
#: src/session/view/sidebar/mod.rs:326
|
#: src/session/view/sidebar/mod.rs:326
|
||||||
msgid "Enable"
|
msgid "Enable"
|
||||||
msgstr "Omogoči"
|
msgstr "Omogoči"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1045
|
#: src/session/view/content/room_details/general_page.rs:1048
|
||||||
msgid "Could not enable encryption"
|
msgid "Could not enable encryption"
|
||||||
msgstr "Šifriranja ni bilo mogoče omogočiti"
|
msgstr "Šifriranja ni bilo mogoče omogočiti"
|
||||||
|
|
||||||
#. Translators: As in, 'Room federated'.
|
#. Translators: As in, 'Room federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1071
|
#: src/session/view/content/room_details/general_page.rs:1107
|
||||||
msgid "Federated"
|
msgid "Federated"
|
||||||
msgstr "Združeno"
|
msgstr "Združeno"
|
||||||
|
|
||||||
#. Translators: As in, 'Room not federated'.
|
#. Translators: As in, 'Room not federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1074
|
#: src/session/view/content/room_details/general_page.rs:1110
|
||||||
msgid "Not federated"
|
msgid "Not federated"
|
||||||
msgstr "Ni združeno"
|
msgstr "Ni združeno"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1105
|
#: src/session/view/content/room_details/general_page.rs:1144
|
||||||
msgid "Room upgraded successfully"
|
msgid "Room upgraded successfully"
|
||||||
msgstr "Klepetalnica uspešno nadgrajena"
|
msgstr "Klepetalnica uspešno nadgrajena"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1109
|
#: src/session/view/content/room_details/general_page.rs:1148
|
||||||
msgid "Could not upgrade room"
|
msgid "Could not upgrade room"
|
||||||
msgstr "Klepetalnice ni bilo mogoče nadgraditi"
|
msgstr "Klepetalnice ni bilo mogoče nadgraditi"
|
||||||
|
|
||||||
@ -3570,7 +3570,7 @@ msgstr "Preklopi iskanje članov klepetalnice"
|
|||||||
msgid "Search for room members"
|
msgid "Search for room members"
|
||||||
msgstr "Poišči člane klepetalnice"
|
msgstr "Poišči člane klepetalnice"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/mod.rs:149
|
#: src/session/view/content/room_details/mod.rs:150
|
||||||
msgid "The user is not in the room members list anymore"
|
msgid "The user is not in the room members list anymore"
|
||||||
msgstr "Uporabnika ni več na seznamu članov klepetalnice"
|
msgstr "Uporabnika ni več na seznamu članov klepetalnice"
|
||||||
|
|
||||||
@ -3680,7 +3680,8 @@ msgid "Change Server Access Control List"
|
|||||||
msgstr "Spremeni seznam za nadzor dostopa do strežnika"
|
msgstr "Spremeni seznam za nadzor dostopa do strežnika"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/permissions/permissions_subpage.ui:191
|
#: src/session/view/content/room_details/permissions/permissions_subpage.ui:191
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:111
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:4
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:43
|
||||||
msgid "Upgrade Room"
|
msgid "Upgrade Room"
|
||||||
msgstr "Nadgradi klepetalnico"
|
msgstr "Nadgradi klepetalnico"
|
||||||
|
|
||||||
@ -3710,20 +3711,51 @@ msgid "Default Power Level"
|
|||||||
msgstr "Privzeta raven moči"
|
msgstr "Privzeta raven moči"
|
||||||
|
|
||||||
#. Translators: As in 'Stable version'.
|
#. Translators: As in 'Stable version'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:85
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:114
|
||||||
msgid "Stable"
|
msgid "Stable"
|
||||||
msgstr "Stabilno"
|
msgstr "Stabilno"
|
||||||
|
|
||||||
#. Translators: As in 'Experimental version'.
|
#. Translators: As in 'Experimental version'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:87
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:117
|
||||||
msgid "Experimental"
|
msgid "Experimental"
|
||||||
msgstr "Poskusno"
|
msgstr "Poskusno"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:94
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:196
|
||||||
msgid "Version"
|
msgid ""
|
||||||
msgstr "Različica"
|
"After the upgrade, you will be the only creator in the room. The other creator will be demoted to the "
|
||||||
|
"default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The other creators will be demoted to the "
|
||||||
|
"default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Drugi avtorji bodo degradirani na privzeto raven moči."
|
||||||
|
msgstr[1] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Drug avtor bo degradiran na privzeto raven moči."
|
||||||
|
msgstr[2] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Druga avtorja bosta degradirana na privzeto raven moči."
|
||||||
|
msgstr[3] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Drugi avtorji bodo degradirani na privzeto raven moči."
|
||||||
|
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:112
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:202
|
||||||
|
msgid ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current creator will be demoted to the "
|
||||||
|
"default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current creators will be demoted to "
|
||||||
|
"the default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Trenutni avtorji bodo degradirani na privzeto raven "
|
||||||
|
"moči."
|
||||||
|
msgstr[1] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Trenutni avtor bo degradiran na privzeto raven moči."
|
||||||
|
msgstr[2] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Trenutna avtorja bosta degradirana na privzeto raven "
|
||||||
|
"moči."
|
||||||
|
msgstr[3] ""
|
||||||
|
"Po nadgradnji boste edini avtor v klepetalnici. Trenutni avtorji bodo degradirani na privzeto raven "
|
||||||
|
"moči."
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:59
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upgrading a room to a more recent version allows to benefit from new features from the Matrix "
|
"Upgrading a room to a more recent version allows to benefit from new features from the Matrix "
|
||||||
"specification. It can also be used to reset the room state, which should make the room faster to join. "
|
"specification. It can also be used to reset the room state, which should make the room faster to join. "
|
||||||
@ -3735,8 +3767,12 @@ msgstr ""
|
|||||||
"klepetalnico hitreje pridružili. Vendar pa je to treba uporabljati zmerno, ker je lahko moteče, saj se "
|
"klepetalnico hitreje pridružili. Vendar pa je to treba uporabljati zmerno, ker je lahko moteče, saj se "
|
||||||
"morajo člani klepetalnice ročno pridružiti novi klepetalnici."
|
"morajo člani klepetalnice ročno pridružiti novi klepetalnici."
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:74
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "Različica"
|
||||||
|
|
||||||
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:118
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:120
|
||||||
msgid "Upgrade"
|
msgid "Upgrade"
|
||||||
msgstr "Nadgradi"
|
msgstr "Nadgradi"
|
||||||
|
|
||||||
@ -3964,7 +4000,7 @@ msgstr "Poslano"
|
|||||||
msgid "Edited"
|
msgid "Edited"
|
||||||
msgstr "Urejeno"
|
msgstr "Urejeno"
|
||||||
|
|
||||||
#: src/session/view/content/room_history/message_row/mod.rs:245
|
#: src/session/view/content/room_history/message_row/mod.rs:243
|
||||||
msgid "Sent at {time}"
|
msgid "Sent at {time}"
|
||||||
msgstr "Poslano {time}"
|
msgstr "Poslano {time}"
|
||||||
|
|
||||||
@ -3990,6 +4026,11 @@ msgstr "Odzivi"
|
|||||||
msgid "In Reply To"
|
msgid "In Reply To"
|
||||||
msgstr "Odgovor na"
|
msgstr "Odgovor na"
|
||||||
|
|
||||||
|
#. Translators: This is a verb, as in 'Mention user'.
|
||||||
|
#: src/session/view/content/room_history/message_row/sender_name.rs:188
|
||||||
|
msgid "Mention"
|
||||||
|
msgstr "Omeni"
|
||||||
|
|
||||||
#. Translators: this is the fallback title for an expander.
|
#. Translators: this is the fallback title for an expander.
|
||||||
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
||||||
msgid "Details"
|
msgid "Details"
|
||||||
|
79
po/uk.po
79
po/uk.po
@ -8,8 +8,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: fractal master\n"
|
"Project-Id-Version: fractal master\n"
|
||||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
||||||
"POT-Creation-Date: 2025-07-30 15:22+0000\n"
|
"POT-Creation-Date: 2025-07-31 17:47+0000\n"
|
||||||
"PO-Revision-Date: 2025-07-30 21:56+0300\n"
|
"PO-Revision-Date: 2025-07-31 21:28+0300\n"
|
||||||
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
|
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
|
||||||
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
|
"Language-Team: Ukrainian <trans-uk@lists.fedoraproject.org>\n"
|
||||||
"Language: uk\n"
|
"Language: uk\n"
|
||||||
@ -687,7 +687,7 @@ msgstr "Полишити"
|
|||||||
#: src/session/view/account_settings/general_page/mod.rs:391
|
#: src/session/view/account_settings/general_page/mod.rs:391
|
||||||
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
||||||
#: src/session/view/content/room_details/general_page.rs:1036
|
#: src/session/view/content/room_details/general_page.rs:1036
|
||||||
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:98
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:112
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:583
|
#: src/session/view/content/room_history/event_actions/group.rs:583
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:647
|
#: src/session/view/content/room_history/event_actions/group.rs:647
|
||||||
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
||||||
@ -1115,7 +1115,6 @@ msgid "Confirm Custom Role"
|
|||||||
msgstr "Підтвердження нетипової ролі"
|
msgstr "Підтвердження нетипової ролі"
|
||||||
|
|
||||||
#: src/components/power_level_selection/row.ui:101
|
#: src/components/power_level_selection/row.ui:101
|
||||||
#| msgid "Advanced Information"
|
|
||||||
msgid "More Information"
|
msgid "More Information"
|
||||||
msgstr "Докладніше"
|
msgstr "Докладніше"
|
||||||
|
|
||||||
@ -1127,11 +1126,11 @@ msgid ""
|
|||||||
"in the room. The only way to demote a creator is to replace the current room "
|
"in the room. The only way to demote a creator is to replace the current room "
|
||||||
"with another one with a different creator, by upgrading the room for example."
|
"with another one with a different creator, by upgrading the room for example."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Автор є незмінною роллю, яку можна пов'язати із декількома користувачами під"
|
"Автор є незмінною роллю, яку можна пов'язати із декількома користувачами під "
|
||||||
" час створення кімнати. Автори завжди мають вищий рівень прав доступу за"
|
"час створення кімнати. Автори завжди мають вищий рівень прав доступу за будь-"
|
||||||
" будь-якого з учасників кімнати, окрім інших авторів, і мають право роботи у"
|
"якого з учасників кімнати, окрім інших авторів, і мають право роботи у "
|
||||||
" кімнаті будь-що. Єдиним способом позбавити автора прав є заміна поточної"
|
"кімнаті будь-що. Єдиним способом позбавити автора прав є заміна поточної "
|
||||||
" кімнати на іншу із іншим автором, наприклад, шляхом оновлення кімнати."
|
"кімнати на іншу із іншим автором, наприклад, шляхом оновлення кімнати."
|
||||||
|
|
||||||
#: src/components/user_page.rs:256
|
#: src/components/user_page.rs:256
|
||||||
#: src/session/view/account_settings/general_page/mod.ui:60
|
#: src/session/view/account_settings/general_page/mod.ui:60
|
||||||
@ -3441,20 +3440,20 @@ msgid "Could not enable encryption"
|
|||||||
msgstr "Не вдалося увімкнути шифрування"
|
msgstr "Не вдалося увімкнути шифрування"
|
||||||
|
|
||||||
#. Translators: As in, 'Room federated'.
|
#. Translators: As in, 'Room federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1095
|
#: src/session/view/content/room_details/general_page.rs:1107
|
||||||
msgid "Federated"
|
msgid "Federated"
|
||||||
msgstr "Інтегрований"
|
msgstr "Інтегрований"
|
||||||
|
|
||||||
#. Translators: As in, 'Room not federated'.
|
#. Translators: As in, 'Room not federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1098
|
#: src/session/view/content/room_details/general_page.rs:1110
|
||||||
msgid "Not federated"
|
msgid "Not federated"
|
||||||
msgstr "Не федеровано"
|
msgstr "Не федеровано"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1132
|
#: src/session/view/content/room_details/general_page.rs:1144
|
||||||
msgid "Room upgraded successfully"
|
msgid "Room upgraded successfully"
|
||||||
msgstr "Кімнату успішно оновлено"
|
msgstr "Кімнату успішно оновлено"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1136
|
#: src/session/view/content/room_details/general_page.rs:1148
|
||||||
msgid "Could not upgrade room"
|
msgid "Could not upgrade room"
|
||||||
msgstr "Не вдалося оновити кімнату"
|
msgstr "Не вдалося оновити кімнату"
|
||||||
|
|
||||||
@ -4019,15 +4018,55 @@ msgid "Default Power Level"
|
|||||||
msgstr "Типовий рівень повноважень"
|
msgstr "Типовий рівень повноважень"
|
||||||
|
|
||||||
#. Translators: As in 'Stable version'.
|
#. Translators: As in 'Stable version'.
|
||||||
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:112
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:114
|
||||||
msgid "Stable"
|
msgid "Stable"
|
||||||
msgstr "Стабільна"
|
msgstr "Стабільна"
|
||||||
|
|
||||||
#. Translators: As in 'Experimental version'.
|
#. Translators: As in 'Experimental version'.
|
||||||
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:115
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:117
|
||||||
msgid "Experimental"
|
msgid "Experimental"
|
||||||
msgstr "Експериментальна"
|
msgstr "Експериментальна"
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:196
|
||||||
|
msgid ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The other "
|
||||||
|
"creator will be demoted to the default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The other "
|
||||||
|
"creators will be demoted to the default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Інших авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[1] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Інших авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[2] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Інших авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[3] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Іншого автора буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:202
|
||||||
|
msgid ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current "
|
||||||
|
"creator will be demoted to the default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current "
|
||||||
|
"creators will be demoted to the default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Поточних авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[1] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Поточних авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[2] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Поточних авторів буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
msgstr[3] ""
|
||||||
|
"Після оновлення ви станете єдиним автором у кімнаті. Поточного автора буде"
|
||||||
|
" звужено у правах до типового рівня доступу."
|
||||||
|
|
||||||
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:59
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:59
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upgrading a room to a more recent version allows to benefit from new "
|
"Upgrading a room to a more recent version allows to benefit from new "
|
||||||
@ -4048,7 +4087,7 @@ msgid "Version"
|
|||||||
msgstr "Версія"
|
msgstr "Версія"
|
||||||
|
|
||||||
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
||||||
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:106
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:120
|
||||||
msgid "Upgrade"
|
msgid "Upgrade"
|
||||||
msgstr "Оновити"
|
msgstr "Оновити"
|
||||||
|
|
||||||
@ -4283,7 +4322,7 @@ msgstr "Надіслано"
|
|||||||
msgid "Edited"
|
msgid "Edited"
|
||||||
msgstr "Змінено"
|
msgstr "Змінено"
|
||||||
|
|
||||||
#: src/session/view/content/room_history/message_row/mod.rs:245
|
#: src/session/view/content/room_history/message_row/mod.rs:243
|
||||||
msgid "Sent at {time}"
|
msgid "Sent at {time}"
|
||||||
msgstr "Надіслано {time}"
|
msgstr "Надіслано {time}"
|
||||||
|
|
||||||
@ -4309,6 +4348,12 @@ msgstr "Реакції"
|
|||||||
msgid "In Reply To"
|
msgid "In Reply To"
|
||||||
msgstr "У відповідь на"
|
msgstr "У відповідь на"
|
||||||
|
|
||||||
|
#. Translators: This is a verb, as in 'Mention user'.
|
||||||
|
#: src/session/view/content/room_history/message_row/sender_name.rs:188
|
||||||
|
#| msgid "_Mention"
|
||||||
|
msgid "Mention"
|
||||||
|
msgstr "Згадка"
|
||||||
|
|
||||||
#. Translators: this is the fallback title for an expander.
|
#. Translators: this is the fallback title for an expander.
|
||||||
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
||||||
msgid "Details"
|
msgid "Details"
|
||||||
|
110
po/zh_CN.po
110
po/zh_CN.po
@ -8,8 +8,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: fractal master\n"
|
"Project-Id-Version: fractal master\n"
|
||||||
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
"Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/fractal/issues\n"
|
||||||
"POT-Creation-Date: 2025-07-28 17:58+0000\n"
|
"POT-Creation-Date: 2025-08-01 15:53+0000\n"
|
||||||
"PO-Revision-Date: 2025-07-29 09:23+0800\n"
|
"PO-Revision-Date: 2025-08-02 08:09+0800\n"
|
||||||
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
|
"Last-Translator: lumingzh <lumingzh@qq.com>\n"
|
||||||
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
|
"Language-Team: Chinese (China) <i18n-zh@googlegroups.com>\n"
|
||||||
"Language: zh_CN\n"
|
"Language: zh_CN\n"
|
||||||
@ -637,8 +637,8 @@ msgstr "离开"
|
|||||||
#: src/identity_verification_view/confirm_qr_code_page.ui:52
|
#: src/identity_verification_view/confirm_qr_code_page.ui:52
|
||||||
#: src/session/view/account_settings/general_page/mod.rs:391
|
#: src/session/view/account_settings/general_page/mod.rs:391
|
||||||
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
#: src/session/view/content/room_details/edit_details_subpage.rs:267
|
||||||
#: src/session/view/content/room_details/general_page.rs:1033
|
#: src/session/view/content/room_details/general_page.rs:1036
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:116
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:112
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:583
|
#: src/session/view/content/room_history/event_actions/group.rs:583
|
||||||
#: src/session/view/content/room_history/event_actions/group.rs:647
|
#: src/session/view/content/room_history/event_actions/group.rs:647
|
||||||
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
#: src/session/view/content/room_history/message_toolbar/mod.ui:76
|
||||||
@ -1938,7 +1938,7 @@ msgid "Any registered user"
|
|||||||
msgstr "任何注册的用户"
|
msgstr "任何注册的用户"
|
||||||
|
|
||||||
#: src/session/model/room/join_rule.rs:288
|
#: src/session/model/room/join_rule.rs:288
|
||||||
#: src/session/view/content/room_details/general_page.rs:939
|
#: src/session/view/content/room_details/general_page.rs:942
|
||||||
msgid "Unsupported rule"
|
msgid "Unsupported rule"
|
||||||
msgstr "不支持的规则"
|
msgstr "不支持的规则"
|
||||||
|
|
||||||
@ -2978,7 +2978,7 @@ msgid "Remove “{address}”"
|
|||||||
msgstr "移除“{address}”"
|
msgstr "移除“{address}”"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/addresses_subpage/mod.rs:431
|
#: src/session/view/content/room_details/addresses_subpage/mod.rs:431
|
||||||
#: src/session/view/content/room_details/general_page.rs:677
|
#: src/session/view/content/room_details/general_page.rs:680
|
||||||
#: src/session/view/create_room_dialog.ui:132
|
#: src/session/view/create_room_dialog.ui:132
|
||||||
msgid "Main Address"
|
msgid "Main Address"
|
||||||
msgstr "主地址"
|
msgstr "主地址"
|
||||||
@ -3124,107 +3124,107 @@ msgstr "说明"
|
|||||||
msgid "Save Description"
|
msgid "Save Description"
|
||||||
msgstr "保存说明"
|
msgstr "保存说明"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:462
|
#: src/session/view/content/room_details/general_page.rs:465
|
||||||
#: src/session/view/content/room_details/general_page.rs:508
|
#: src/session/view/content/room_details/general_page.rs:511
|
||||||
#: src/session/view/content/room_details/invite_subpage/list.rs:260
|
#: src/session/view/content/room_details/invite_subpage/list.rs:260
|
||||||
msgid "Member"
|
msgid "Member"
|
||||||
msgid_plural "Members"
|
msgid_plural "Members"
|
||||||
msgstr[0] "成员"
|
msgstr[0] "成员"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:632
|
#: src/session/view/content/room_details/general_page.rs:635
|
||||||
msgid "Could not change notifications setting"
|
msgid "Could not change notifications setting"
|
||||||
msgstr "无法更改通知设置"
|
msgstr "无法更改通知设置"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:672
|
#: src/session/view/content/room_details/general_page.rs:675
|
||||||
#: src/session/view/content/room_details/general_page.rs:711
|
#: src/session/view/content/room_details/general_page.rs:714
|
||||||
msgid "Copy address"
|
msgid "Copy address"
|
||||||
msgstr "复制地址"
|
msgstr "复制地址"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:673
|
#: src/session/view/content/room_details/general_page.rs:676
|
||||||
#: src/session/view/content/room_details/general_page.rs:712
|
#: src/session/view/content/room_details/general_page.rs:715
|
||||||
msgid "Address copied to clipboard"
|
msgid "Address copied to clipboard"
|
||||||
msgstr "地址已复制到剪贴板"
|
msgstr "地址已复制到剪贴板"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:754
|
#: src/session/view/content/room_details/general_page.rs:757
|
||||||
msgid "Room link copied to clipboard"
|
msgid "Room link copied to clipboard"
|
||||||
msgstr "聊天室链接已复制到剪贴板"
|
msgstr "聊天室链接已复制到剪贴板"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:815
|
#: src/session/view/content/room_details/general_page.rs:818
|
||||||
msgid "Could not change guest access"
|
msgid "Could not change guest access"
|
||||||
msgstr "无法更改访客访问权限"
|
msgstr "无法更改访客访问权限"
|
||||||
|
|
||||||
#. Translators: Do NOT translate the content between '{' and '}',
|
#. Translators: Do NOT translate the content between '{' and '}',
|
||||||
#. this is a variable name.
|
#. this is a variable name.
|
||||||
#: src/session/view/content/room_details/general_page.rs:832
|
#: src/session/view/content/room_details/general_page.rs:835
|
||||||
msgid "Publish in the {homeserver} directory"
|
msgid "Publish in the {homeserver} directory"
|
||||||
msgstr "公布在 {homeserver} 目录"
|
msgstr "公布在 {homeserver} 目录"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:907
|
#: src/session/view/content/room_details/general_page.rs:910
|
||||||
msgid "Could not publish room in directory"
|
msgid "Could not publish room in directory"
|
||||||
msgstr "无法在目录公布聊天室"
|
msgstr "无法在目录公布聊天室"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:909
|
#: src/session/view/content/room_details/general_page.rs:912
|
||||||
msgid "Could not unpublish room from directory"
|
msgid "Could not unpublish room from directory"
|
||||||
msgstr "无法从目录撤下聊天室"
|
msgstr "无法从目录撤下聊天室"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:930
|
#: src/session/view/content/room_details/general_page.rs:933
|
||||||
#: src/session/view/content/room_details/general_page.ui:274
|
#: src/session/view/content/room_details/general_page.ui:274
|
||||||
msgid "Anyone, even if they are not in the room"
|
msgid "Anyone, even if they are not in the room"
|
||||||
msgstr "任何人,即使他们未加入聊天室"
|
msgstr "任何人,即使他们未加入聊天室"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:933
|
#: src/session/view/content/room_details/general_page.rs:936
|
||||||
#: src/session/view/content/room_details/general_page.ui:275
|
#: src/session/view/content/room_details/general_page.ui:275
|
||||||
msgid "Members only, since this option was selected"
|
msgid "Members only, since this option was selected"
|
||||||
msgstr "仅成员,在选择该选项之后"
|
msgstr "仅成员,在选择该选项之后"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:935
|
#: src/session/view/content/room_details/general_page.rs:938
|
||||||
#: src/session/view/content/room_details/general_page.ui:277
|
#: src/session/view/content/room_details/general_page.ui:277
|
||||||
msgid "Members only, since they were invited"
|
msgid "Members only, since they were invited"
|
||||||
msgstr "仅成员,在他们被邀请之后"
|
msgstr "仅成员,在他们被邀请之后"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:937
|
#: src/session/view/content/room_details/general_page.rs:940
|
||||||
#: src/session/view/content/room_details/general_page.ui:276
|
#: src/session/view/content/room_details/general_page.ui:276
|
||||||
msgid "Members only, since they joined the room"
|
msgid "Members only, since they joined the room"
|
||||||
msgstr "仅成员,在他们加入聊天室后"
|
msgstr "仅成员,在他们加入聊天室后"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:986
|
#: src/session/view/content/room_details/general_page.rs:989
|
||||||
msgid "Could not change who can read history"
|
msgid "Could not change who can read history"
|
||||||
msgstr "无法更改历史查阅权限"
|
msgstr "无法更改历史查阅权限"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1028
|
#: src/session/view/content/room_details/general_page.rs:1031
|
||||||
msgid "Enable Encryption?"
|
msgid "Enable Encryption?"
|
||||||
msgstr "启用加密吗?"
|
msgstr "启用加密吗?"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1029
|
#: src/session/view/content/room_details/general_page.rs:1032
|
||||||
msgid ""
|
msgid ""
|
||||||
"Enabling encryption will prevent new members to read the history before they "
|
"Enabling encryption will prevent new members to read the history before they "
|
||||||
"arrived. This cannot be disabled later."
|
"arrived. This cannot be disabled later."
|
||||||
msgstr "启用加密将阻止新成员在到达聊天室前查阅聊天历史。该选项无法在随后禁用。"
|
msgstr "启用加密将阻止新成员在到达聊天室前查阅聊天历史。该选项无法在随后禁用。"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1034
|
#: src/session/view/content/room_details/general_page.rs:1037
|
||||||
#: src/session/view/sidebar/mod.rs:320 src/session/view/sidebar/mod.rs:326
|
#: src/session/view/sidebar/mod.rs:320 src/session/view/sidebar/mod.rs:326
|
||||||
msgid "Enable"
|
msgid "Enable"
|
||||||
msgstr "启用"
|
msgstr "启用"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1045
|
#: src/session/view/content/room_details/general_page.rs:1048
|
||||||
msgid "Could not enable encryption"
|
msgid "Could not enable encryption"
|
||||||
msgstr "无法启用加密"
|
msgstr "无法启用加密"
|
||||||
|
|
||||||
#. Translators: As in, 'Room federated'.
|
#. Translators: As in, 'Room federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1071
|
#: src/session/view/content/room_details/general_page.rs:1107
|
||||||
msgid "Federated"
|
msgid "Federated"
|
||||||
msgstr "联合的"
|
msgstr "联合的"
|
||||||
|
|
||||||
#. Translators: As in, 'Room not federated'.
|
#. Translators: As in, 'Room not federated'.
|
||||||
#: src/session/view/content/room_details/general_page.rs:1074
|
#: src/session/view/content/room_details/general_page.rs:1110
|
||||||
msgid "Not federated"
|
msgid "Not federated"
|
||||||
msgstr "未联合"
|
msgstr "未联合"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1105
|
#: src/session/view/content/room_details/general_page.rs:1144
|
||||||
msgid "Room upgraded successfully"
|
msgid "Room upgraded successfully"
|
||||||
msgstr "聊天室已成功升级"
|
msgstr "聊天室已成功升级"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/general_page.rs:1109
|
#: src/session/view/content/room_details/general_page.rs:1148
|
||||||
msgid "Could not upgrade room"
|
msgid "Could not upgrade room"
|
||||||
msgstr "无法升级聊天室"
|
msgstr "无法升级聊天室"
|
||||||
|
|
||||||
@ -3621,7 +3621,7 @@ msgstr "切换聊天室成员搜索"
|
|||||||
msgid "Search for room members"
|
msgid "Search for room members"
|
||||||
msgstr "搜索聊天室成员"
|
msgstr "搜索聊天室成员"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/mod.rs:149
|
#: src/session/view/content/room_details/mod.rs:150
|
||||||
msgid "The user is not in the room members list anymore"
|
msgid "The user is not in the room members list anymore"
|
||||||
msgstr "该用户不再在聊天室成员列表中"
|
msgstr "该用户不再在聊天室成员列表中"
|
||||||
|
|
||||||
@ -3732,7 +3732,8 @@ msgid "Change Server Access Control List"
|
|||||||
msgstr "更改服务器访问控制列表"
|
msgstr "更改服务器访问控制列表"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/permissions/permissions_subpage.ui:191
|
#: src/session/view/content/room_details/permissions/permissions_subpage.ui:191
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:111
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:4
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:43
|
||||||
msgid "Upgrade Room"
|
msgid "Upgrade Room"
|
||||||
msgstr "升级聊天室"
|
msgstr "升级聊天室"
|
||||||
|
|
||||||
@ -3762,20 +3763,38 @@ msgid "Default Power Level"
|
|||||||
msgstr "默认权力等级"
|
msgstr "默认权力等级"
|
||||||
|
|
||||||
#. Translators: As in 'Stable version'.
|
#. Translators: As in 'Stable version'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:85
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:114
|
||||||
msgid "Stable"
|
msgid "Stable"
|
||||||
msgstr "稳定版"
|
msgstr "稳定版"
|
||||||
|
|
||||||
#. Translators: As in 'Experimental version'.
|
#. Translators: As in 'Experimental version'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:87
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:117
|
||||||
msgid "Experimental"
|
msgid "Experimental"
|
||||||
msgstr "体验版"
|
msgstr "体验版"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:94
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:196
|
||||||
msgid "Version"
|
msgid ""
|
||||||
msgstr "版本"
|
"After the upgrade, you will be the only creator in the room. The other "
|
||||||
|
"creator will be demoted to the default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The other "
|
||||||
|
"creators will be demoted to the default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"升级之后,您将成为该聊天室中唯一的创建者。其他创建者将被降级为默认的权力级"
|
||||||
|
"别。"
|
||||||
|
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:112
|
#: src/session/view/content/room_details/upgrade_dialog/mod.rs:202
|
||||||
|
msgid ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current "
|
||||||
|
"creator will be demoted to the default power level."
|
||||||
|
msgid_plural ""
|
||||||
|
"After the upgrade, you will be the only creator in the room. The current "
|
||||||
|
"creators will be demoted to the default power level."
|
||||||
|
msgstr[0] ""
|
||||||
|
"升级之后,您将成为该聊天室中唯一的创建者。当前的创建者将被降级为默认的权力级"
|
||||||
|
"别。"
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:59
|
||||||
msgid ""
|
msgid ""
|
||||||
"Upgrading a room to a more recent version allows to benefit from new "
|
"Upgrading a room to a more recent version allows to benefit from new "
|
||||||
"features from the Matrix specification. It can also be used to reset the "
|
"features from the Matrix specification. It can also be used to reset the "
|
||||||
@ -3787,8 +3806,12 @@ msgstr ""
|
|||||||
"态,可使加入聊天室速度更快。然而由于其可能引起混乱应保守使用,因为聊天室成员"
|
"态,可使加入聊天室速度更快。然而由于其可能引起混乱应保守使用,因为聊天室成员"
|
||||||
"需要手动加入新聊天室。"
|
"需要手动加入新聊天室。"
|
||||||
|
|
||||||
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:74
|
||||||
|
msgid "Version"
|
||||||
|
msgstr "版本"
|
||||||
|
|
||||||
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
#. Translators: In this string, 'Upgrade' is a verb, as in 'Upgrade Room'.
|
||||||
#: src/session/view/content/room_details/room_upgrade_dialog.rs:118
|
#: src/session/view/content/room_details/upgrade_dialog/mod.ui:120
|
||||||
msgid "Upgrade"
|
msgid "Upgrade"
|
||||||
msgstr "升级"
|
msgstr "升级"
|
||||||
|
|
||||||
@ -4018,7 +4041,7 @@ msgstr "已发送"
|
|||||||
msgid "Edited"
|
msgid "Edited"
|
||||||
msgstr "已编辑"
|
msgstr "已编辑"
|
||||||
|
|
||||||
#: src/session/view/content/room_history/message_row/mod.rs:245
|
#: src/session/view/content/room_history/message_row/mod.rs:243
|
||||||
msgid "Sent at {time}"
|
msgid "Sent at {time}"
|
||||||
msgstr "在 {time} 发送"
|
msgstr "在 {time} 发送"
|
||||||
|
|
||||||
@ -4041,6 +4064,11 @@ msgstr "回应"
|
|||||||
msgid "In Reply To"
|
msgid "In Reply To"
|
||||||
msgstr "在回复中"
|
msgstr "在回复中"
|
||||||
|
|
||||||
|
#. Translators: This is a verb, as in 'Mention user'.
|
||||||
|
#: src/session/view/content/room_history/message_row/sender_name.rs:188
|
||||||
|
msgid "Mention"
|
||||||
|
msgstr "提到"
|
||||||
|
|
||||||
#. Translators: this is the fallback title for an expander.
|
#. Translators: this is the fallback title for an expander.
|
||||||
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
#: src/session/view/content/room_history/message_row/text/widgets.rs:439
|
||||||
msgid "Details"
|
msgid "Details"
|
||||||
|
@ -6,7 +6,7 @@ use tracing::error;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
spawn, spawn_tokio,
|
spawn, spawn_tokio,
|
||||||
utils::{CountedRef, File},
|
utils::{CountedRef, File, TokioDrop},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
@ -19,9 +19,12 @@ mod imp {
|
|||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct AnimatedImagePaintable {
|
pub struct AnimatedImagePaintable {
|
||||||
/// The image loader.
|
/// The image decoder.
|
||||||
image_loader: OnceCell<Arc<Image<'static>>>,
|
decoder: OnceCell<Arc<TokioDrop<Image>>>,
|
||||||
/// The file of the image.
|
/// The file of the image.
|
||||||
|
///
|
||||||
|
/// We need to keep a strong reference to the temporary file or it will
|
||||||
|
/// be destroyed.
|
||||||
file: OnceCell<File>,
|
file: OnceCell<File>,
|
||||||
/// The current frame that is displayed.
|
/// The current frame that is displayed.
|
||||||
pub(super) current_frame: RefCell<Option<Arc<Frame>>>,
|
pub(super) current_frame: RefCell<Option<Arc<Frame>>>,
|
||||||
@ -49,7 +52,7 @@ mod imp {
|
|||||||
self.current_frame
|
self.current_frame
|
||||||
.borrow()
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| self.image_loader().info().height, |f| f.height())
|
.map_or_else(|| self.decoder().details().height(), |f| f.height())
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap_or(i32::MAX)
|
.unwrap_or(i32::MAX)
|
||||||
}
|
}
|
||||||
@ -58,7 +61,7 @@ mod imp {
|
|||||||
self.current_frame
|
self.current_frame
|
||||||
.borrow()
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| self.image_loader().info().width, |f| f.width())
|
.map_or_else(|| self.decoder().details().width(), |f| f.width())
|
||||||
.try_into()
|
.try_into()
|
||||||
.unwrap_or(i32::MAX)
|
.unwrap_or(i32::MAX)
|
||||||
}
|
}
|
||||||
@ -94,26 +97,27 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AnimatedImagePaintable {
|
impl AnimatedImagePaintable {
|
||||||
/// The image loader.
|
/// The image decoder.
|
||||||
fn image_loader(&self) -> &Arc<Image<'static>> {
|
fn decoder(&self) -> &Arc<TokioDrop<Image>> {
|
||||||
self.image_loader
|
self.decoder.get().expect("decoder should be initialized")
|
||||||
.get()
|
|
||||||
.expect("image loader is initialized")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize the image.
|
/// Initialize the image.
|
||||||
pub(super) fn init(
|
pub(super) fn init(
|
||||||
&self,
|
&self,
|
||||||
file: File,
|
decoder: Arc<TokioDrop<Image>>,
|
||||||
image_loader: Arc<Image<'static>>,
|
|
||||||
first_frame: Arc<Frame>,
|
first_frame: Arc<Frame>,
|
||||||
|
file: Option<File>,
|
||||||
) {
|
) {
|
||||||
self.file.set(file).expect("file is uninitialized");
|
self.decoder
|
||||||
self.image_loader
|
.set(decoder)
|
||||||
.set(image_loader)
|
.expect("decoder should be uninitialized");
|
||||||
.expect("image loader is uninitialized");
|
|
||||||
self.current_frame.replace(Some(first_frame));
|
self.current_frame.replace(Some(first_frame));
|
||||||
|
|
||||||
|
if let Some(file) = file {
|
||||||
|
self.file.set(file).expect("file should be uninitialized");
|
||||||
|
}
|
||||||
|
|
||||||
self.update_animation();
|
self.update_animation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,9 +202,9 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn load_next_frame_inner(&self) {
|
async fn load_next_frame_inner(&self) {
|
||||||
let image = self.image_loader().clone();
|
let decoder = self.decoder().clone();
|
||||||
|
|
||||||
let result = spawn_tokio!(async move { image.next_frame().await })
|
let result = spawn_tokio!(async move { decoder.next_frame().await })
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@ -229,16 +233,16 @@ glib::wrapper! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AnimatedImagePaintable {
|
impl AnimatedImagePaintable {
|
||||||
/// Construct an `AnimatedImagePaintable` with the given loader and first
|
/// Construct an `AnimatedImagePaintable` with the given decoder, first
|
||||||
/// frame.
|
/// frame, and the file containing the image, if any.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
file: File,
|
decoder: Arc<TokioDrop<Image>>,
|
||||||
image_loader: Arc<Image<'static>>,
|
|
||||||
first_frame: Arc<Frame>,
|
first_frame: Arc<Frame>,
|
||||||
|
file: Option<File>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let obj = glib::Object::new::<Self>();
|
let obj = glib::Object::new::<Self>();
|
||||||
|
|
||||||
obj.imp().init(file, image_loader, first_frame);
|
obj.imp().init(decoder, first_frame, file);
|
||||||
|
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
@ -316,7 +316,7 @@ mod imp {
|
|||||||
|
|
||||||
if let Some(send_state) = item.send_state() {
|
if let Some(send_state) = item.send_state() {
|
||||||
match send_state {
|
match send_state {
|
||||||
EventSendState::NotSentYet => return MessageState::Sending,
|
EventSendState::NotSentYet { .. } => return MessageState::Sending,
|
||||||
EventSendState::SendingFailed {
|
EventSendState::SendingFailed {
|
||||||
error,
|
error,
|
||||||
is_recoverable,
|
is_recoverable,
|
||||||
|
@ -58,10 +58,6 @@ pub enum SessionState {
|
|||||||
Ready = 2,
|
Ready = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, glib::Boxed)]
|
|
||||||
#[boxed_type(name = "BoxedClient")]
|
|
||||||
pub struct BoxedClient(Client);
|
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use std::cell::{Cell, OnceCell, RefCell};
|
use std::cell::{Cell, OnceCell, RefCell};
|
||||||
|
|
||||||
@ -70,9 +66,8 @@ mod imp {
|
|||||||
#[derive(Debug, Default, glib::Properties)]
|
#[derive(Debug, Default, glib::Properties)]
|
||||||
#[properties(wrapper_type = super::Session)]
|
#[properties(wrapper_type = super::Session)]
|
||||||
pub struct Session {
|
pub struct Session {
|
||||||
/// The Matrix client.
|
/// The Matrix client for this session.
|
||||||
#[property(construct_only)]
|
client: OnceCell<TokioDrop<Client>>,
|
||||||
client: TokioDrop<BoxedClient>,
|
|
||||||
/// The list model of the sidebar.
|
/// The list model of the sidebar.
|
||||||
#[property(get = Self::sidebar_list_model)]
|
#[property(get = Self::sidebar_list_model)]
|
||||||
sidebar_list_model: OnceCell<SidebarListModel>,
|
sidebar_list_model: OnceCell<SidebarListModel>,
|
||||||
@ -128,27 +123,6 @@ mod imp {
|
|||||||
|
|
||||||
#[glib::derived_properties]
|
#[glib::derived_properties]
|
||||||
impl ObjectImpl for Session {
|
impl ObjectImpl for Session {
|
||||||
fn constructed(&self) {
|
|
||||||
self.parent_constructed();
|
|
||||||
let obj = self.obj();
|
|
||||||
|
|
||||||
self.ignored_users.set_session(Some(obj.clone()));
|
|
||||||
self.notifications.set_session(Some(obj.clone()));
|
|
||||||
self.user_sessions.init(&obj, obj.user_id().clone());
|
|
||||||
|
|
||||||
let monitor = gio::NetworkMonitor::default();
|
|
||||||
let handler_id = monitor.connect_network_changed(clone!(
|
|
||||||
#[weak(rename_to = imp)]
|
|
||||||
self,
|
|
||||||
move |_, _| {
|
|
||||||
spawn!(async move {
|
|
||||||
imp.update_homeserver_reachable().await;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
));
|
|
||||||
self.network_monitor_handler_id.replace(Some(handler_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dispose(&self) {
|
fn dispose(&self) {
|
||||||
// Needs to be disconnected or else it may restart the sync
|
// Needs to be disconnected or else it may restart the sync
|
||||||
if let Some(handler_id) = self.network_monitor_handler_id.take() {
|
if let Some(handler_id) = self.network_monitor_handler_id.take() {
|
||||||
@ -176,9 +150,34 @@ mod imp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
// The Matrix client.
|
/// Set the Matrix client for this session.
|
||||||
|
pub(super) fn set_client(&self, client: Client) {
|
||||||
|
self.client
|
||||||
|
.set(TokioDrop::new(client))
|
||||||
|
.expect("client should be uninitialized");
|
||||||
|
|
||||||
|
let obj = self.obj();
|
||||||
|
|
||||||
|
self.ignored_users.set_session(Some(obj.clone()));
|
||||||
|
self.notifications.set_session(Some(obj.clone()));
|
||||||
|
self.user_sessions.init(&obj, obj.user_id().clone());
|
||||||
|
|
||||||
|
let monitor = gio::NetworkMonitor::default();
|
||||||
|
let handler_id = monitor.connect_network_changed(clone!(
|
||||||
|
#[weak(rename_to = imp)]
|
||||||
|
self,
|
||||||
|
move |_, _| {
|
||||||
|
spawn!(async move {
|
||||||
|
imp.update_homeserver_reachable().await;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
self.network_monitor_handler_id.replace(Some(handler_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Matrix client for this session.
|
||||||
pub(super) fn client(&self) -> &Client {
|
pub(super) fn client(&self) -> &Client {
|
||||||
&self.client.get().expect("session should be restored").0
|
self.client.get().expect("client should be initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The list model of the sidebar.
|
/// The list model of the sidebar.
|
||||||
@ -462,6 +461,7 @@ mod imp {
|
|||||||
|
|
||||||
let sync_settings = SyncSettings::new()
|
let sync_settings = SyncSettings::new()
|
||||||
.timeout(Duration::from_secs(30))
|
.timeout(Duration::from_secs(30))
|
||||||
|
.ignore_timeout_on_first_sync(true)
|
||||||
.filter(filter.into());
|
.filter(filter.into());
|
||||||
|
|
||||||
let mut sync_stream = Box::pin(client.sync_stream(sync_settings).await);
|
let mut sync_stream = Box::pin(client.sync_stream(sync_settings).await);
|
||||||
@ -751,11 +751,13 @@ impl Session {
|
|||||||
.await
|
.await
|
||||||
.expect("task was not aborted")?;
|
.expect("task was not aborted")?;
|
||||||
|
|
||||||
Ok(glib::Object::builder()
|
let obj = glib::Object::builder::<Self>()
|
||||||
.property("info", stored_session)
|
.property("info", stored_session)
|
||||||
.property("settings", settings)
|
.property("settings", settings)
|
||||||
.property("client", BoxedClient(client))
|
.build();
|
||||||
.build())
|
obj.imp().set_client(client);
|
||||||
|
|
||||||
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new session from the session of the given Matrix client.
|
/// Create a new session from the session of the given Matrix client.
|
||||||
|
@ -953,7 +953,7 @@ mod imp {
|
|||||||
row.set_read_only(!is_supported || !can_change);
|
row.set_read_only(!is_supported || !can_change);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the history_visibility of the room.
|
/// Set the history visibility of the room.
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
async fn set_history_visibility(&self) {
|
async fn set_history_visibility(&self) {
|
||||||
let Some(room) = self.room.obj() else {
|
let Some(room) = self.room.obj() else {
|
||||||
|
@ -733,7 +733,7 @@ mod imp {
|
|||||||
self.update_changed();
|
self.update_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle when the redact_own row has changed.
|
/// Handle when the `redact_own` row has changed.
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn redact_own_changed(&self) {
|
fn redact_own_changed(&self) {
|
||||||
if self.update_in_progress.get() {
|
if self.update_in_progress.get() {
|
||||||
@ -757,7 +757,7 @@ mod imp {
|
|||||||
self.update_changed();
|
self.update_changed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle when the redact_others row has changed.
|
/// Handle when the `redact_others` row has changed.
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn redact_others_changed(&self) {
|
fn redact_others_changed(&self) {
|
||||||
if self.update_in_progress.get() {
|
if self.update_in_progress.get() {
|
||||||
|
@ -813,8 +813,7 @@ mod imp {
|
|||||||
}
|
}
|
||||||
future::Either::Right((response, _)) => {
|
future::Either::Right((response, _)) => {
|
||||||
// The linux location stream requires a tokio executor when dropped.
|
// The linux location stream requires a tokio executor when dropped.
|
||||||
let stream_drop = TokioDrop::new();
|
let _ = TokioDrop::new(location_stream);
|
||||||
let _ = stream_drop.set(location_stream);
|
|
||||||
|
|
||||||
if response == gtk::ResponseType::Ok {
|
if response == gtk::ResponseType::Ok {
|
||||||
break;
|
break;
|
||||||
|
@ -290,7 +290,10 @@ mod imp {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
groups.into_iter().for_each(|group| group.process_batch());
|
|
||||||
|
for group in groups {
|
||||||
|
group.process_batch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle when items were removed in the underlying model.
|
/// Handle when items were removed in the underlying model.
|
||||||
|
@ -127,7 +127,7 @@ impl AnySyncOrStrippedTimelineEvent {
|
|||||||
let ev = match raw {
|
let ev = match raw {
|
||||||
RawAnySyncOrStrippedTimelineEvent::Sync(ev) => Self::Sync(ev.deserialize()?.into()),
|
RawAnySyncOrStrippedTimelineEvent::Sync(ev) => Self::Sync(ev.deserialize()?.into()),
|
||||||
RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => {
|
RawAnySyncOrStrippedTimelineEvent::Stripped(ev) => {
|
||||||
Self::Stripped(ev.deserialize()?.into())
|
Self::Stripped(Box::new(ev.deserialize_as()?))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -28,7 +28,10 @@ pub(crate) use queue::{IMAGE_QUEUE, ImageRequestPriority};
|
|||||||
|
|
||||||
use super::{FrameDimensions, MediaFileError};
|
use super::{FrameDimensions, MediaFileError};
|
||||||
use crate::{
|
use crate::{
|
||||||
DISABLE_GLYCIN_SANDBOX, RUNTIME, components::AnimatedImagePaintable, spawn_tokio, utils::File,
|
DISABLE_GLYCIN_SANDBOX, RUNTIME,
|
||||||
|
components::AnimatedImagePaintable,
|
||||||
|
spawn_tokio,
|
||||||
|
utils::{File, TokioDrop, save_data_to_tmp_file},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The maximum dimensions of a thumbnail in the timeline.
|
/// The maximum dimensions of a thumbnail in the timeline.
|
||||||
@ -64,35 +67,66 @@ const THUMBNAIL_DIMENSIONS_THRESHOLD: u32 = 200;
|
|||||||
/// [supported image formats of glycin]: https://gitlab.gnome.org/GNOME/glycin/-/tree/main?ref_type=heads#supported-image-formats
|
/// [supported image formats of glycin]: https://gitlab.gnome.org/GNOME/glycin/-/tree/main?ref_type=heads#supported-image-formats
|
||||||
const SUPPORTED_ANIMATED_IMAGE_MIME_TYPES: &[&str] = &["image/gif", "image/png", "image/webp"];
|
const SUPPORTED_ANIMATED_IMAGE_MIME_TYPES: &[&str] = &["image/gif", "image/png", "image/webp"];
|
||||||
|
|
||||||
/// Get an image loader for the given file.
|
/// The source for decoding an image.
|
||||||
async fn image_loader(file: gio::File) -> Result<glycin::Image<'static>, glycin::ErrorCtx> {
|
enum ImageDecoderSource {
|
||||||
let mut loader = glycin::Loader::new(file);
|
/// The bytes containing the encoded image.
|
||||||
|
Data(Vec<u8>),
|
||||||
|
/// The file containing the encoded image.
|
||||||
|
File(File),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageDecoderSource {
|
||||||
|
/// The maximum size of the `Data` variant. This is 1 MB.
|
||||||
|
const MAX_DATA_SIZE: usize = 1_048_576;
|
||||||
|
|
||||||
|
/// Construct an `ImageSource` from the given bytes.
|
||||||
|
///
|
||||||
|
/// If the size of the bytes are too big to be kept in memory, they are
|
||||||
|
/// written to a temporary file.
|
||||||
|
async fn with_bytes(bytes: Vec<u8>) -> Result<Self, MediaFileError> {
|
||||||
|
if bytes.len() > Self::MAX_DATA_SIZE {
|
||||||
|
Ok(Self::File(save_data_to_tmp_file(bytes).await?))
|
||||||
|
} else {
|
||||||
|
Ok(Self::Data(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert this image source into a loader.
|
||||||
|
///
|
||||||
|
/// Returns the created loader, and the image file, if any.
|
||||||
|
fn into_loader(self) -> (glycin::Loader, Option<File>) {
|
||||||
|
let (mut loader, file) = match self {
|
||||||
|
Self::Data(bytes) => (glycin::Loader::new_vec(bytes), None),
|
||||||
|
Self::File(file) => (glycin::Loader::new(file.as_gfile()), Some(file)),
|
||||||
|
};
|
||||||
|
|
||||||
if DISABLE_GLYCIN_SANDBOX {
|
if DISABLE_GLYCIN_SANDBOX {
|
||||||
loader.sandbox_selector(glycin::SandboxSelector::NotSandboxed);
|
loader.sandbox_selector(glycin::SandboxSelector::NotSandboxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn_tokio!(async move { loader.load().await })
|
(loader, file)
|
||||||
.await
|
}
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Load the given file as an image into a `GdkPaintable`.
|
/// Decode this image source into an [`Image`].
|
||||||
///
|
///
|
||||||
/// Set `request_dimensions` if the image will be shown at specific dimensions.
|
/// Set `request_dimensions` if the image will be shown at specific
|
||||||
/// To show the image at its natural size, set it to `None`.
|
/// dimensions. To show the image at its natural size, set it to `None`.
|
||||||
async fn load_image(
|
async fn decode_image(
|
||||||
file: File,
|
self,
|
||||||
request_dimensions: Option<FrameDimensions>,
|
request_dimensions: Option<FrameDimensions>,
|
||||||
) -> Result<Image, glycin::ErrorCtx> {
|
) -> Result<Image, ImageError> {
|
||||||
let image_loader = image_loader(file.as_gfile()).await?;
|
let (loader, file) = self.into_loader();
|
||||||
|
|
||||||
|
let decoder = spawn_tokio!(async move { loader.load().await })
|
||||||
|
.await
|
||||||
|
.expect("task was not aborted")?;
|
||||||
|
|
||||||
let frame_request = request_dimensions.map(|request| {
|
let frame_request = request_dimensions.map(|request| {
|
||||||
let image_info = image_loader.info();
|
let image_details = decoder.details();
|
||||||
|
|
||||||
let original_dimensions = FrameDimensions {
|
let original_dimensions = FrameDimensions {
|
||||||
width: image_info.width,
|
width: image_details.width(),
|
||||||
height: image_info.height,
|
height: image_details.height(),
|
||||||
};
|
};
|
||||||
|
|
||||||
original_dimensions.to_image_loader_request(request)
|
original_dimensions.to_image_loader_request(request)
|
||||||
@ -100,27 +134,44 @@ async fn load_image(
|
|||||||
|
|
||||||
spawn_tokio!(async move {
|
spawn_tokio!(async move {
|
||||||
let first_frame = if let Some(frame_request) = frame_request {
|
let first_frame = if let Some(frame_request) = frame_request {
|
||||||
image_loader.specific_frame(frame_request).await?
|
decoder.specific_frame(frame_request).await?
|
||||||
} else {
|
} else {
|
||||||
image_loader.next_frame().await?
|
decoder.next_frame().await?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Image {
|
Ok(Image {
|
||||||
file,
|
file,
|
||||||
loader: image_loader.into(),
|
decoder: TokioDrop::new(decoder).into(),
|
||||||
first_frame: first_frame.into(),
|
first_frame: first_frame.into(),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.expect("task was not aborted")
|
.expect("task was not aborted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<File> for ImageDecoderSource {
|
||||||
|
fn from(value: File) -> Self {
|
||||||
|
Self::File(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<gio::File> for ImageDecoderSource {
|
||||||
|
fn from(value: gio::File) -> Self {
|
||||||
|
Self::File(value.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An image that was just loaded.
|
/// An image that was just loaded.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct Image {
|
pub(crate) struct Image {
|
||||||
/// The file of the image.
|
/// The file containing the image, if any.
|
||||||
file: File,
|
///
|
||||||
/// The image loader.
|
/// We need to keep a strong reference to the temporary file or it will be
|
||||||
loader: Arc<glycin::Image<'static>>,
|
/// destroyed.
|
||||||
|
file: Option<File>,
|
||||||
|
/// The image decoder.
|
||||||
|
decoder: Arc<TokioDrop<glycin::Image>>,
|
||||||
/// The first frame of the image.
|
/// The first frame of the image.
|
||||||
first_frame: Arc<glycin::Frame>,
|
first_frame: Arc<glycin::Frame>,
|
||||||
}
|
}
|
||||||
@ -134,7 +185,7 @@ impl fmt::Debug for Image {
|
|||||||
impl From<Image> for gdk::Paintable {
|
impl From<Image> for gdk::Paintable {
|
||||||
fn from(value: Image) -> Self {
|
fn from(value: Image) -> Self {
|
||||||
if value.first_frame.delay().is_some() {
|
if value.first_frame.delay().is_some() {
|
||||||
AnimatedImagePaintable::new(value.file, value.loader, value.first_frame).upcast()
|
AnimatedImagePaintable::new(value.decoder, value.first_frame, value.file).upcast()
|
||||||
} else {
|
} else {
|
||||||
value.first_frame.texture().upcast()
|
value.first_frame.texture().upcast()
|
||||||
}
|
}
|
||||||
@ -157,9 +208,14 @@ impl ImageInfoLoader {
|
|||||||
async fn into_first_frame(self) -> Option<Frame> {
|
async fn into_first_frame(self) -> Option<Frame> {
|
||||||
match self {
|
match self {
|
||||||
Self::File(file) => {
|
Self::File(file) => {
|
||||||
let image_loader = image_loader(file).await.ok()?;
|
let (loader, _) = ImageDecoderSource::from(file).into_loader();
|
||||||
let handle = spawn_tokio!(async move { image_loader.next_frame().await });
|
|
||||||
Some(Frame::Glycin(handle.await.unwrap().ok()?))
|
let frame = spawn_tokio!(async move { loader.load().await?.next_frame().await })
|
||||||
|
.await
|
||||||
|
.expect("task was not aborted")
|
||||||
|
.ok()?;
|
||||||
|
|
||||||
|
Some(Frame::Glycin(frame))
|
||||||
}
|
}
|
||||||
Self::Texture(texture) => Some(Frame::Texture(texture)),
|
Self::Texture(texture) => Some(Frame::Texture(texture)),
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,12 @@ use tokio::{
|
|||||||
};
|
};
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
|
|
||||||
use super::{Image, ImageError, load_image};
|
use super::{Image, ImageDecoderSource, ImageError};
|
||||||
use crate::{
|
use crate::{
|
||||||
spawn_tokio,
|
spawn_tokio,
|
||||||
utils::{
|
utils::{
|
||||||
File,
|
File,
|
||||||
media::{FrameDimensions, MediaFileError},
|
media::{FrameDimensions, MediaFileError},
|
||||||
save_data_to_tmp_file,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ impl ImageRequestQueueInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Add the given request to the queue.
|
/// Add the given request to the queue.
|
||||||
fn add_request(&mut self, request_id: ImageRequestId, request: ImageRequest) {
|
fn queue_request(&mut self, request_id: ImageRequestId, request: ImageRequest) {
|
||||||
let is_limit_reached = self.is_limit_reached();
|
let is_limit_reached = self.is_limit_reached();
|
||||||
if !is_limit_reached || request.priority == ImageRequestPriority::High {
|
if !is_limit_reached || request.priority == ImageRequestPriority::High {
|
||||||
// Spawn the request right away.
|
// Spawn the request right away.
|
||||||
@ -175,6 +174,31 @@ impl ImageRequestQueueInner {
|
|||||||
self.requests.insert(request_id, request);
|
self.requests.insert(request_id, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the given image request.
|
||||||
|
///
|
||||||
|
/// If another request for the same image already exists, this will reuse
|
||||||
|
/// the same request.
|
||||||
|
fn add_request(
|
||||||
|
&mut self,
|
||||||
|
inner: ImageLoaderRequest,
|
||||||
|
priority: ImageRequestPriority,
|
||||||
|
) -> ImageRequestHandle {
|
||||||
|
let request_id = inner.source.request_id();
|
||||||
|
|
||||||
|
// If the request already exists, use the existing one.
|
||||||
|
if let Some(request) = self.requests.get(&request_id) {
|
||||||
|
let result_receiver = request.result_sender.subscribe();
|
||||||
|
return ImageRequestHandle::new(result_receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build and add the request.
|
||||||
|
let (request, result_receiver) = ImageRequest::new(inner, priority);
|
||||||
|
|
||||||
|
self.queue_request(request_id.clone(), request);
|
||||||
|
|
||||||
|
ImageRequestHandle::new(result_receiver)
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a request to download an image.
|
/// Add a request to download an image.
|
||||||
///
|
///
|
||||||
/// If another request for the same image already exists, this will reuse
|
/// If another request for the same image already exists, this will reuse
|
||||||
@ -186,24 +210,13 @@ impl ImageRequestQueueInner {
|
|||||||
dimensions: Option<FrameDimensions>,
|
dimensions: Option<FrameDimensions>,
|
||||||
priority: ImageRequestPriority,
|
priority: ImageRequestPriority,
|
||||||
) -> ImageRequestHandle {
|
) -> ImageRequestHandle {
|
||||||
let data = DownloadRequestData {
|
self.add_request(
|
||||||
client,
|
ImageLoaderRequest {
|
||||||
settings,
|
source: ImageRequestSource::Download(DownloadRequest { client, settings }),
|
||||||
dimensions,
|
dimensions,
|
||||||
};
|
},
|
||||||
let request_id = data.request_id();
|
priority,
|
||||||
|
)
|
||||||
// If the request already exists, use the existing one.
|
|
||||||
if let Some(request) = self.requests.get(&request_id) {
|
|
||||||
let result_receiver = request.result_sender.subscribe();
|
|
||||||
return ImageRequestHandle::new(result_receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and add the request.
|
|
||||||
let (request, result_receiver) = ImageRequest::new(data, priority);
|
|
||||||
self.add_request(request_id.clone(), request);
|
|
||||||
|
|
||||||
ImageRequestHandle::new(result_receiver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a request to load an image from a file.
|
/// Add a request to load an image from a file.
|
||||||
@ -215,23 +228,15 @@ impl ImageRequestQueueInner {
|
|||||||
file: File,
|
file: File,
|
||||||
dimensions: Option<FrameDimensions>,
|
dimensions: Option<FrameDimensions>,
|
||||||
) -> ImageRequestHandle {
|
) -> ImageRequestHandle {
|
||||||
let data = FileRequestData { file, dimensions };
|
|
||||||
let request_id = data.request_id();
|
|
||||||
|
|
||||||
// If the request already exists, use the existing one.
|
|
||||||
if let Some(request) = self.requests.get(&request_id) {
|
|
||||||
let result_receiver = request.result_sender.subscribe();
|
|
||||||
return ImageRequestHandle::new(result_receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build and add the request.
|
|
||||||
// Always use high priority because file requests should always be for
|
// Always use high priority because file requests should always be for
|
||||||
// previewing a local image.
|
// previewing a local image.
|
||||||
let (request, result_receiver) = ImageRequest::new(data, ImageRequestPriority::High);
|
self.add_request(
|
||||||
|
ImageLoaderRequest {
|
||||||
self.add_request(request_id.clone(), request);
|
source: ImageRequestSource::File(file),
|
||||||
|
dimensions,
|
||||||
ImageRequestHandle::new(result_receiver)
|
},
|
||||||
|
ImageRequestPriority::High,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark the request with the given ID as stalled.
|
/// Mark the request with the given ID as stalled.
|
||||||
@ -335,8 +340,8 @@ impl ImageRequestQueueInner {
|
|||||||
|
|
||||||
/// A request for an image.
|
/// A request for an image.
|
||||||
struct ImageRequest {
|
struct ImageRequest {
|
||||||
/// The data of the request.
|
/// The request to the image loader.
|
||||||
data: ImageRequestData,
|
inner: ImageLoaderRequest,
|
||||||
/// The priority of the request.
|
/// The priority of the request.
|
||||||
priority: ImageRequestPriority,
|
priority: ImageRequestPriority,
|
||||||
/// The sender of the channel to use to send the result.
|
/// The sender of the channel to use to send the result.
|
||||||
@ -352,13 +357,13 @@ struct ImageRequest {
|
|||||||
impl ImageRequest {
|
impl ImageRequest {
|
||||||
/// Construct an image request with the given data and priority.
|
/// Construct an image request with the given data and priority.
|
||||||
fn new(
|
fn new(
|
||||||
data: impl Into<ImageRequestData>,
|
inner: ImageLoaderRequest,
|
||||||
priority: ImageRequestPriority,
|
priority: ImageRequestPriority,
|
||||||
) -> (Self, broadcast::Receiver<Result<Image, ImageError>>) {
|
) -> (Self, broadcast::Receiver<Result<Image, ImageError>>) {
|
||||||
let (result_sender, result_receiver) = broadcast::channel(1);
|
let (result_sender, result_receiver) = broadcast::channel(1);
|
||||||
(
|
(
|
||||||
Self {
|
Self {
|
||||||
data: data.into(),
|
inner,
|
||||||
priority,
|
priority,
|
||||||
result_sender,
|
result_sender,
|
||||||
retries_count: 0,
|
retries_count: 0,
|
||||||
@ -379,14 +384,14 @@ impl ImageRequest {
|
|||||||
|
|
||||||
/// Spawn this request.
|
/// Spawn this request.
|
||||||
fn spawn(&self) {
|
fn spawn(&self) {
|
||||||
let data = self.data.clone();
|
let inner = self.inner.clone();
|
||||||
let result_sender = self.result_sender.clone();
|
let result_sender = self.result_sender.clone();
|
||||||
let retries_count = self.retries_count;
|
let retries_count = self.retries_count;
|
||||||
let task_handle = self.task_handle.clone();
|
let task_handle = self.task_handle.clone();
|
||||||
let stalled_timeout_source = self.stalled_timeout_source.clone();
|
let stalled_timeout_source = self.stalled_timeout_source.clone();
|
||||||
|
|
||||||
let abort_handle = spawn_tokio!(async move {
|
let abort_handle = spawn_tokio!(async move {
|
||||||
let request_id = data.request_id();
|
let request_id = inner.source.request_id();
|
||||||
|
|
||||||
let stalled_timeout_source_clone = stalled_timeout_source.clone();
|
let stalled_timeout_source_clone = stalled_timeout_source.clone();
|
||||||
let request_id_clone = request_id.clone();
|
let request_id_clone = request_id.clone();
|
||||||
@ -404,7 +409,7 @@ impl ImageRequest {
|
|||||||
source.remove();
|
source.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = data.await;
|
let result = inner.await;
|
||||||
|
|
||||||
// Cancel the timeout.
|
// Cancel the timeout.
|
||||||
if let Ok(Some(source)) = stalled_timeout_source.lock().map(|mut s| s.take()) {
|
if let Ok(Some(source)) = stalled_timeout_source.lock().map(|mut s| s.take()) {
|
||||||
@ -451,7 +456,7 @@ impl Drop for ImageRequest {
|
|||||||
handle.abort();
|
handle.abort();
|
||||||
|
|
||||||
// Broadcast that the request was aborted.
|
// Broadcast that the request was aborted.
|
||||||
let request_id = self.data.request_id();
|
let request_id = self.inner.source.request_id();
|
||||||
let result_sender = self.result_sender.clone();
|
let result_sender = self.result_sender.clone();
|
||||||
spawn_tokio!(async move {
|
spawn_tokio!(async move {
|
||||||
if let Err(error) = result_sender.send(Err(ImageError::Aborted)) {
|
if let Err(error) = result_sender.send(Err(ImageError::Aborted)) {
|
||||||
@ -462,26 +467,17 @@ impl Drop for ImageRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data of a request to download an image.
|
/// A request to download an image.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct DownloadRequestData {
|
struct DownloadRequest {
|
||||||
/// The Matrix client to use to make the request.
|
/// The Matrix client to use to make the request.
|
||||||
client: Client,
|
client: Client,
|
||||||
/// The settings of the request.
|
/// The settings of the request.
|
||||||
settings: MediaRequestParameters,
|
settings: MediaRequestParameters,
|
||||||
/// The dimensions to request.
|
|
||||||
dimensions: Option<FrameDimensions>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DownloadRequestData {
|
impl IntoFuture for DownloadRequest {
|
||||||
/// The ID of the image request with this data.
|
type Output = Result<ImageDecoderSource, MediaFileError>;
|
||||||
fn request_id(&self) -> ImageRequestId {
|
|
||||||
ImageRequestId::Download(self.settings.unique_key())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoFuture for DownloadRequestData {
|
|
||||||
type Output = Result<File, MediaFileError>;
|
|
||||||
type IntoFuture = BoxFuture<'static, Self::Output>;
|
type IntoFuture = BoxFuture<'static, Self::Output>;
|
||||||
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
fn into_future(self) -> Self::IntoFuture {
|
||||||
@ -491,155 +487,76 @@ impl IntoFuture for DownloadRequestData {
|
|||||||
|
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let media = client.media();
|
let media = client.media();
|
||||||
let data = match media.get_media_content(&settings, true).await {
|
let data = media
|
||||||
Ok(data) => data,
|
.get_media_content(&settings, true)
|
||||||
Err(error) => {
|
.await
|
||||||
return Err(MediaFileError::from(error));
|
.map_err(MediaFileError::from)?;
|
||||||
}
|
|
||||||
};
|
let file = ImageDecoderSource::with_bytes(data).await?;
|
||||||
|
|
||||||
let file = save_data_to_tmp_file(data).await?;
|
|
||||||
Ok(file)
|
Ok(file)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The data of a request to load an image file into a paintable.
|
/// A request to the image loader.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct FileRequestData {
|
struct ImageLoaderRequest {
|
||||||
/// The image file to load.
|
/// The source of the image data.
|
||||||
file: File,
|
source: ImageRequestSource,
|
||||||
/// The dimensions to request.
|
/// The dimensions to request.
|
||||||
dimensions: Option<FrameDimensions>,
|
dimensions: Option<FrameDimensions>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileRequestData {
|
impl IntoFuture for ImageLoaderRequest {
|
||||||
/// The ID of the image request with this data.
|
|
||||||
fn request_id(&self) -> ImageRequestId {
|
|
||||||
ImageRequestId::File(self.file.path().expect("file has a path"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoFuture for FileRequestData {
|
|
||||||
type Output = Result<Image, glycin::ErrorCtx>;
|
|
||||||
type IntoFuture = BoxFuture<'static, Self::Output>;
|
|
||||||
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
|
||||||
let Self { file, dimensions } = self;
|
|
||||||
|
|
||||||
Box::pin(async move { load_image(file, dimensions).await })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The data of an image request.
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum ImageRequestData {
|
|
||||||
/// The data for a download request.
|
|
||||||
Download {
|
|
||||||
/// The data to download the image.
|
|
||||||
download_data: DownloadRequestData,
|
|
||||||
/// The data to load the image into a paintable, after it was
|
|
||||||
/// downloaded.
|
|
||||||
file_data: Option<FileRequestData>,
|
|
||||||
},
|
|
||||||
/// The data for a file request.
|
|
||||||
File(FileRequestData),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageRequestData {
|
|
||||||
/// The ID of the image request with this data.
|
|
||||||
fn request_id(&self) -> ImageRequestId {
|
|
||||||
match self {
|
|
||||||
ImageRequestData::Download { download_data, .. } => download_data.request_id(),
|
|
||||||
ImageRequestData::File(file_data) => file_data.request_id(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The data for the next request with this image request data.
|
|
||||||
fn into_next_request_data(self) -> DownloadOrFileRequestData {
|
|
||||||
match self {
|
|
||||||
Self::Download {
|
|
||||||
download_data,
|
|
||||||
file_data,
|
|
||||||
} => {
|
|
||||||
if let Some(file_data) = file_data {
|
|
||||||
file_data.into()
|
|
||||||
} else {
|
|
||||||
download_data.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::File(file_data) => file_data.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoFuture for ImageRequestData {
|
|
||||||
type Output = Result<Image, ImageError>;
|
type Output = Result<Image, ImageError>;
|
||||||
type IntoFuture = BoxFuture<'static, Self::Output>;
|
type IntoFuture = BoxFuture<'static, Self::Output>;
|
||||||
|
|
||||||
fn into_future(self) -> Self::IntoFuture {
|
fn into_future(self) -> Self::IntoFuture {
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let file_data = match self.into_next_request_data() {
|
// Load the data from the source.
|
||||||
DownloadOrFileRequestData::Download(download_data) => {
|
let source = self.source.try_into_decoder_source().await?;
|
||||||
let dimensions = download_data.dimensions;
|
|
||||||
|
|
||||||
// Download the image to a file.
|
// Decode the image from the data.
|
||||||
match download_data.await {
|
source
|
||||||
Ok(file) => FileRequestData { file, dimensions },
|
.decode_image(self.dimensions)
|
||||||
Err(error) => {
|
.await
|
||||||
warn!("Could not retrieve image: {error}");
|
.inspect_err(|error| warn!("Could not decode image: {error}"))
|
||||||
return Err(error.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DownloadOrFileRequestData::File(file_data) => file_data,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load the image from the file.
|
|
||||||
match file_data.clone().await {
|
|
||||||
Ok(image) => Ok(image),
|
|
||||||
Err(error) => {
|
|
||||||
warn!("Could not load image from file: {error}");
|
|
||||||
Err(error.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DownloadRequestData> for ImageRequestData {
|
/// The source for an image request.
|
||||||
fn from(download_data: DownloadRequestData) -> Self {
|
|
||||||
Self::Download {
|
|
||||||
download_data,
|
|
||||||
file_data: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FileRequestData> for ImageRequestData {
|
|
||||||
fn from(value: FileRequestData) -> Self {
|
|
||||||
Self::File(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The data of a download request or a file request.
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum DownloadOrFileRequestData {
|
enum ImageRequestSource {
|
||||||
/// The data for a download request.
|
/// The image must be downloaded from the media cache or the server.
|
||||||
Download(DownloadRequestData),
|
Download(DownloadRequest),
|
||||||
/// The data for a file request.
|
/// The image is in the given file.
|
||||||
File(FileRequestData),
|
File(File),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DownloadRequestData> for DownloadOrFileRequestData {
|
impl ImageRequestSource {
|
||||||
fn from(download_data: DownloadRequestData) -> Self {
|
/// The ID of the image request with this source.
|
||||||
Self::Download(download_data)
|
fn request_id(&self) -> ImageRequestId {
|
||||||
|
match self {
|
||||||
|
Self::Download(download_request) => {
|
||||||
|
ImageRequestId::Download(download_request.settings.unique_key())
|
||||||
|
}
|
||||||
|
Self::File(file) => ImageRequestId::File(file.path().expect("file should have a path")),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl From<FileRequestData> for DownloadOrFileRequestData {
|
/// Try to download the image, if necessary.
|
||||||
fn from(value: FileRequestData) -> Self {
|
async fn try_into_decoder_source(self) -> Result<ImageDecoderSource, ImageError> {
|
||||||
Self::File(value)
|
match self {
|
||||||
|
Self::Download(download_request) => {
|
||||||
|
// Download the image.
|
||||||
|
Ok(download_request
|
||||||
|
.await
|
||||||
|
.inspect_err(|error| warn!("Could not retrieve image: {error}"))?)
|
||||||
|
}
|
||||||
|
Self::File(data) => Ok(data.into()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,8 +3,9 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cell::{Cell, OnceCell, RefCell},
|
cell::{Cell, OnceCell, RefCell},
|
||||||
fmt, fs, io,
|
fmt, fs,
|
||||||
io::Write,
|
io::{self, Write},
|
||||||
|
ops::Deref,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::{Arc, LazyLock},
|
sync::{Arc, LazyLock},
|
||||||
@ -384,36 +385,30 @@ impl<T> AsyncAction<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type that requires the tokio runtime to be running when dropped.
|
/// A wrapper that requires the tokio runtime to be running when dropped.
|
||||||
///
|
|
||||||
/// This is basically usable as a [`OnceCell`].
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TokioDrop<T>(OnceCell<T>);
|
pub struct TokioDrop<T>(Option<T>);
|
||||||
|
|
||||||
impl<T> TokioDrop<T> {
|
impl<T> TokioDrop<T> {
|
||||||
/// Create a new empty `TokioDrop`;
|
/// Create a new `TokioDrop` wrapping the given type.
|
||||||
pub fn new() -> Self {
|
pub fn new(value: T) -> Self {
|
||||||
Self::default()
|
Self(Some(value))
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets a reference to the underlying value.
|
|
||||||
///
|
|
||||||
/// Returns `None` if the cell is empty.
|
|
||||||
pub fn get(&self) -> Option<&T> {
|
|
||||||
self.0.get()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the contents of this cell to `value`.
|
|
||||||
///
|
|
||||||
/// Returns `Ok(())` if the cell was empty and `Err(value)` if it was full.
|
|
||||||
pub(crate) fn set(&self, value: T) -> Result<(), T> {
|
|
||||||
self.0.set(value)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for TokioDrop<T> {
|
impl<T> Deref for TokioDrop<T> {
|
||||||
fn default() -> Self {
|
type Target = T;
|
||||||
Self(Default::default())
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.0
|
||||||
|
.as_ref()
|
||||||
|
.expect("TokioDrop should always contain a value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> From<T> for TokioDrop<T> {
|
||||||
|
fn from(value: T) -> Self {
|
||||||
|
Self::new(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,35 +416,12 @@ impl<T> Drop for TokioDrop<T> {
|
|||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let _guard = RUNTIME.enter();
|
let _guard = RUNTIME.enter();
|
||||||
|
|
||||||
if let Some(inner) = self.0.take() {
|
if let Some(value) = self.0.take() {
|
||||||
drop(inner);
|
drop(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: glib::property::Property> glib::property::Property for TokioDrop<T> {
|
|
||||||
type Value = T::Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> glib::property::PropertyGet for TokioDrop<T> {
|
|
||||||
type Value = T;
|
|
||||||
|
|
||||||
fn get<R, F: Fn(&Self::Value) -> R>(&self, f: F) -> R {
|
|
||||||
f(self.get().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> glib::property::PropertySet for TokioDrop<T> {
|
|
||||||
type SetValue = T;
|
|
||||||
|
|
||||||
fn set(&self, v: Self::SetValue) {
|
|
||||||
assert!(
|
|
||||||
self.set(v).is_ok(),
|
|
||||||
"TokioDrop value was already initialized"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The state of a resource that can be loaded.
|
/// The state of a resource that can be loaded.
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, glib::Enum)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, glib::Enum)]
|
||||||
#[enum_type(name = "LoadingState")]
|
#[enum_type(name = "LoadingState")]
|
||||||
@ -539,6 +511,7 @@ pub(crate) async fn save_data_to_tmp_file(data: Vec<u8>) -> Result<File, std::io
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut file = NamedTempFile::new_in(dir)?;
|
let mut file = NamedTempFile::new_in(dir)?;
|
||||||
|
tracing::debug!("Created new tmp file: {}", file.path().to_string_lossy());
|
||||||
file.write_all(&data)?;
|
file.write_all(&data)?;
|
||||||
|
|
||||||
Ok(file.into())
|
Ok(file.into())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user