mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-15 05:06:53 +00:00
feat: Show dehydrated devices in account panel
This commit is contained in:
6 files changed
+64
-35
No files matched your search
@@ -70,6 +70,7 @@ enum AccountBody {
|
||||
email_requirement: EmailRequirement,
|
||||
email: Option<String>,
|
||||
devices: Vec<DeviceCard>,
|
||||
dehydrated_device_id: Option<OwnedDeviceId>,
|
||||
},
|
||||
Locked,
|
||||
}
|
||||
@@ -132,7 +133,8 @@ async fn get_account(
|
||||
oidc_enabled: services.oidc.enabled(),
|
||||
email_requirement,
|
||||
email,
|
||||
devices: device_cards
|
||||
devices: device_cards,
|
||||
dehydrated_device_id,
|
||||
}))
|
||||
}
|
||||
|
||||
|
||||
@@ -67,12 +67,15 @@ impl Avatar {
|
||||
pub(super) fn for_device(
|
||||
client_metadata: Option<&ClientMetadata>,
|
||||
display_name: Option<&str>,
|
||||
dehydrated: bool,
|
||||
) -> Self {
|
||||
let avatar_src = client_metadata
|
||||
.and_then(|metadata| metadata.logo_uri.as_ref())
|
||||
.map(|uri| uri.as_str().to_owned());
|
||||
|
||||
let avatar_type = if let Some(avatar_src) = avatar_src {
|
||||
let avatar_type = if dehydrated {
|
||||
AvatarType::Initial('⊡')
|
||||
} else if let Some(avatar_src) = avatar_src {
|
||||
AvatarType::Image(avatar_src)
|
||||
} else if let Some(initial) = display_name.and_then(|name| name.chars().next()) {
|
||||
if client_metadata.is_some() {
|
||||
@@ -130,6 +133,7 @@ pub(super) struct DeviceCard {
|
||||
pub last_active: String,
|
||||
pub oauth_metadata: Option<ClientMetadata>,
|
||||
pub style: DeviceCardStyle,
|
||||
pub dehydrated: bool,
|
||||
}
|
||||
|
||||
impl HtmlSafe for DeviceCard {}
|
||||
@@ -163,12 +167,21 @@ impl DeviceCard {
|
||||
}
|
||||
.await;
|
||||
|
||||
let dehydrated_device_id = services
|
||||
.users
|
||||
.get_dehydrated_device(user_id)
|
||||
.await
|
||||
.ok()
|
||||
.map(|device| device.device_id);
|
||||
let dehydrated = dehydrated_device_id.as_ref() == Some(&device.device_id);
|
||||
|
||||
let display_name = oauth_metadata
|
||||
.as_ref()
|
||||
.and_then(|metadata| metadata.client_name.clone())
|
||||
.or_else(|| device.display_name.clone());
|
||||
|
||||
let avatar = Avatar::for_device(oauth_metadata.as_ref(), display_name.as_deref());
|
||||
let avatar =
|
||||
Avatar::for_device(oauth_metadata.as_ref(), display_name.as_deref(), dehydrated);
|
||||
|
||||
let last_active = device.last_seen_ts.map_or_else(
|
||||
|| "unknown".to_owned(),
|
||||
@@ -190,6 +203,7 @@ impl DeviceCard {
|
||||
last_active,
|
||||
oauth_metadata,
|
||||
style,
|
||||
dehydrated,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
<div class="card">
|
||||
{{ avatar }}
|
||||
<div class="info">
|
||||
<div class="name">
|
||||
<span>
|
||||
{% if let Some(display_name) = display_name %}
|
||||
{{ display_name }}
|
||||
{% else %}
|
||||
Unknown device
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if style == DeviceCardStyle::Detailed %}
|
||||
<span class="id">
|
||||
<span class="mobile-hidden">•</span>
|
||||
<ul class="bullet-separated">
|
||||
<li>{{ device_id }}</li>
|
||||
<li>
|
||||
{% if let Some(metadata) = oauth_metadata %}
|
||||
<a href="{{ metadata.client_uri }}">Client website</a>
|
||||
{% else %}
|
||||
legacy
|
||||
{% endif %}
|
||||
</li>
|
||||
</span>
|
||||
{% if dehydrated %}
|
||||
<div class="name">Dehydrated device</div>
|
||||
{% else %}
|
||||
<div class="name">
|
||||
<span>
|
||||
{% if let Some(display_name) = display_name %}
|
||||
{{ display_name }}
|
||||
{% else %}
|
||||
Unknown device
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if style == DeviceCardStyle::Detailed %}
|
||||
<span class="id">
|
||||
<span class="mobile-hidden">•</span>
|
||||
<ul class="bullet-separated">
|
||||
<li>{{ device_id }}</li>
|
||||
<li>
|
||||
{% if let Some(metadata) = oauth_metadata %}
|
||||
<a href="{{ metadata.client_uri }}">Client website</a>
|
||||
{% else %}
|
||||
legacy
|
||||
{% endif %}
|
||||
</li>
|
||||
</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
Last active: {{ last_active }}
|
||||
</div>
|
||||
<div>
|
||||
{% if style != DeviceCardStyle::Detailed %}
|
||||
<a href="{{ crate::ROUTE_PREFIX }}/account/device/{{ device_id }}/">Details</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
Last active: {{ last_active }}
|
||||
</div>
|
||||
<div>
|
||||
{% if style != DeviceCardStyle::Detailed %}
|
||||
<a href="{{ crate::ROUTE_PREFIX }}/account/device/{{ device_id }}/">Details</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -9,7 +9,7 @@ Your account
|
||||
<h1>Manage your account</h1>
|
||||
{{ user_card }}
|
||||
{% match body %}
|
||||
{% when AccountBody::Unlocked { suspended, email_requirement, email, devices, oidc_enabled } %}
|
||||
{% when AccountBody::Unlocked { suspended, email_requirement, email, devices, oidc_enabled, dehydrated_device_id } %}
|
||||
{% if suspended %}
|
||||
<p class="card danger">
|
||||
⚠️ Your account has been suspended by your homeserver's administrator.
|
||||
@@ -52,6 +52,9 @@ Your account
|
||||
and sign in to start chatting on Matrix.
|
||||
</span>
|
||||
{% endfor %}
|
||||
{% if let Some(dehydrated_device_id) = dehydrated_device_id %}
|
||||
<small>⊡ Your account has a dehydrated device. <a href="device/{{ dehydrated_device_id | urlencode_strict }}/">View details</a></small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
</section>
|
||||
|
||||
@@ -27,6 +27,8 @@ Device information
|
||||
{% if let Some((_, session_info)) = client_metadata %}
|
||||
This device has permission to:
|
||||
{{ ClientScopes { scopes: session_info.scopes.clone() } }}
|
||||
{% else if device_card.dehydrated %}
|
||||
This is your dehydrated device. It saves encryption keys for you while you're offline.
|
||||
{% else %}
|
||||
This device can access and control all features of your Matrix account.
|
||||
<br>
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::{
|
||||
time::{Duration, SystemTime},
|
||||
};
|
||||
|
||||
use askama::filters::urlencode_strict;
|
||||
use axum::{
|
||||
extract::FromRequestParts,
|
||||
http::request::Parts,
|
||||
@@ -63,8 +64,11 @@ impl LoginTarget {
|
||||
| Self::DeviceCode(code) =>
|
||||
format!("oauth2/grant/device_code?{}", serde_urlencoded::to_string(code).unwrap())
|
||||
.into(),
|
||||
| Self::DeviceInfo(path) => format!("account/device/{}/", path.device).into(),
|
||||
| Self::RemoveDevice(path) => format!("account/device/{}/remove", path.device).into(),
|
||||
| Self::DeviceInfo(path) =>
|
||||
format!("account/device/{}/", urlencode_strict(&path.device).unwrap()).into(),
|
||||
| Self::RemoveDevice(path) =>
|
||||
format!("account/device/{}/remove", urlencode_strict(&path.device).unwrap())
|
||||
.into(),
|
||||
};
|
||||
|
||||
format!("{ROUTE_PREFIX}/{path}")
|
||||
|
||||
Reference in new issue
Block a user