You've already forked AstralRinth
feat: add notifs onto friends ws temporarily (#6290)
* feat: add notifs onto friends ws temporarily * fix: lint + styling * fix: regressions
This commit is contained in:
@@ -1648,6 +1648,8 @@ export namespace Labrinth {
|
||||
message_id?: string
|
||||
invited_by?: string
|
||||
organization_id?: string
|
||||
server_id?: string
|
||||
server_name?: string
|
||||
team_id?: string
|
||||
role?: string
|
||||
old_status?: string
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::event::{
|
||||
LoadingPayload, ProcessPayload, ProfilePayload, WarningPayload,
|
||||
};
|
||||
use futures::prelude::*;
|
||||
use serde_json::Value;
|
||||
#[cfg(feature = "tauri")]
|
||||
use tauri::{Emitter, Manager};
|
||||
use uuid::Uuid;
|
||||
@@ -303,6 +304,20 @@ pub async fn emit_friend(payload: FriendPayload) -> crate::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub async fn emit_notification(payload: Value) -> crate::Result<()> {
|
||||
#[cfg(feature = "tauri")]
|
||||
{
|
||||
let event_state = crate::EventState::get()?;
|
||||
event_state
|
||||
.app
|
||||
.emit("notification", payload)
|
||||
.map_err(EventError::from)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// loading_join! macro
|
||||
// loading_join!(key: Option<&LoadingBarId>, total: f64, message: Option<&str>; task1, task2, task3...)
|
||||
// This will submit a loading event with the given message for each task as they complete
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::ErrorKind;
|
||||
use crate::data::ModrinthCredentials;
|
||||
use crate::event::FriendPayload;
|
||||
use crate::event::emit::emit_friend;
|
||||
use crate::event::emit::{emit_friend, emit_notification};
|
||||
use crate::state::tunnel::InternalTunnelSocket;
|
||||
use crate::state::{ProcessManager, Profile, TunnelSocket};
|
||||
use crate::util::fetch::{FetchSemaphore, fetch_advanced, fetch_json};
|
||||
@@ -22,6 +22,7 @@ use futures::{SinkExt, StreamExt};
|
||||
use reqwest::Method;
|
||||
use reqwest::header::HeaderValue;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use std::net::SocketAddr;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
@@ -120,16 +121,34 @@ impl FriendsSocket {
|
||||
Ok(msg) => {
|
||||
let server_message = match msg {
|
||||
Message::Text(text) => {
|
||||
ServerToClientMessage::deserialize(
|
||||
match ServerToClientMessage::deserialize(
|
||||
Either::Left(&text),
|
||||
)
|
||||
.ok()
|
||||
) {
|
||||
Ok(message) => Some(message),
|
||||
Err(_) => {
|
||||
if let Ok(notification) =
|
||||
serde_json::from_str::<Value>(&text)
|
||||
{
|
||||
let _ = Self::handle_notification(notification).await;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::Binary(bytes) => {
|
||||
ServerToClientMessage::deserialize(
|
||||
match ServerToClientMessage::deserialize(
|
||||
Either::Right(&bytes),
|
||||
)
|
||||
.ok()
|
||||
) {
|
||||
Ok(message) => Some(message),
|
||||
Err(_) => {
|
||||
if let Ok(notification) =
|
||||
serde_json::from_slice::<Value>(&bytes)
|
||||
{
|
||||
let _ = Self::handle_notification(notification).await;
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::Ping(bytes) => {
|
||||
if let Some(write) = write_handle
|
||||
@@ -224,6 +243,19 @@ impl FriendsSocket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_notification(notification: Value) -> crate::Result<()> {
|
||||
if notification
|
||||
.get("body")
|
||||
.and_then(|body| body.get("type"))
|
||||
.and_then(Value::as_str)
|
||||
.is_some()
|
||||
{
|
||||
emit_notification(notification).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn socket_loop() -> crate::Result<()> {
|
||||
let state = crate::State::get().await?;
|
||||
|
||||
@@ -18,28 +18,35 @@
|
||||
>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
<div class="flex items-center justify-between gap-2.5">
|
||||
<div class="flex items-center gap-2">
|
||||
<div
|
||||
class="flex items-center"
|
||||
:class="{
|
||||
'text-red': item.type === 'error',
|
||||
'text-orange': item.type === 'warning',
|
||||
'text-green': item.type === 'download',
|
||||
'text-contrast': item.type === 'success',
|
||||
'text-blue':
|
||||
!item.type ||
|
||||
!['error', 'warning', 'success', 'download'].includes(item.type),
|
||||
}"
|
||||
>
|
||||
<IssuesIcon v-if="item.type === 'warning'" class="h-5 w-5" />
|
||||
<DownloadIcon v-else-if="item.type === 'download'" class="h-5 w-5" />
|
||||
<CheckCircleIcon v-else-if="item.type === 'success'" class="h-5 w-5" />
|
||||
<XCircleIcon v-else-if="item.type === 'error'" class="h-5 w-5" />
|
||||
<InfoIcon v-else class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="text-contrast font-semibold m-0 grow">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="flex min-w-0 flex-1 items-center gap-2">
|
||||
<component
|
||||
:is="item.titleLogo"
|
||||
v-if="item.titleLogo"
|
||||
class="h-7 w-auto min-w-0 max-w-full text-contrast"
|
||||
/>
|
||||
<template v-else>
|
||||
<div
|
||||
class="flex items-center"
|
||||
:class="{
|
||||
'text-red': item.type === 'error',
|
||||
'text-orange': item.type === 'warning',
|
||||
'text-green': item.type === 'download',
|
||||
'text-contrast': item.type === 'success',
|
||||
'text-blue':
|
||||
!item.type ||
|
||||
!['error', 'warning', 'success', 'download'].includes(item.type),
|
||||
}"
|
||||
>
|
||||
<IssuesIcon v-if="item.type === 'warning'" class="h-5 w-5" />
|
||||
<DownloadIcon v-else-if="item.type === 'download'" class="h-5 w-5" />
|
||||
<CheckCircleIcon v-else-if="item.type === 'success'" class="h-5 w-5" />
|
||||
<XCircleIcon v-else-if="item.type === 'error'" class="h-5 w-5" />
|
||||
<InfoIcon v-else class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="text-contrast font-semibold m-0 grow">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<ButtonStyled size="small" type="transparent" circular>
|
||||
<button @click="dismiss(item.id)">
|
||||
@@ -50,6 +57,11 @@
|
||||
<span v-if="item.text" class="text-primary">
|
||||
{{ item.text }}
|
||||
</span>
|
||||
<component
|
||||
:is="item.bodyComponent"
|
||||
v-if="item.bodyComponent"
|
||||
v-bind="item.bodyProps ?? {}"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="item.progressItems?.length" class="flex flex-col gap-3">
|
||||
<div
|
||||
@@ -89,6 +101,7 @@
|
||||
:color="btn.color || (idx === 0 ? 'brand' : undefined)"
|
||||
>
|
||||
<button @click="handleButtonClick(item.id, btn)">
|
||||
<component :is="btn.icon" v-if="btn.icon" />
|
||||
{{ btn.label }}
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
@@ -166,7 +179,8 @@ withDefaults(
|
||||
top: calc(var(--top-bar-height, 3rem) + 1.5rem);
|
||||
right: 1.5rem;
|
||||
z-index: 200;
|
||||
width: 400px;
|
||||
width: 520px;
|
||||
max-width: calc(100vw - 3rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
@@ -179,6 +193,7 @@ withDefaults(
|
||||
@media screen and (max-width: 500px) {
|
||||
.popup-notification-group {
|
||||
width: calc(100% - 1.5rem);
|
||||
max-width: none;
|
||||
right: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ export * from './icons'
|
||||
export { default as InstallingBanner } from './InstallingBanner.vue'
|
||||
export * from './labels'
|
||||
export * from './marketing'
|
||||
export { default as ModrinthHostingLogo } from './ModrinthServersIcon.vue'
|
||||
export { default as ModrinthServersIcon } from './ModrinthServersIcon.vue'
|
||||
export { default as SaveBanner } from './SaveBanner.vue'
|
||||
export * from './server-header'
|
||||
export { default as ServerListEmpty } from './server-list-empty/ServerListEmpty.vue'
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { Component } from 'vue'
|
||||
|
||||
import { createContext } from '.'
|
||||
|
||||
export interface PopupNotificationButton {
|
||||
label: string
|
||||
action: () => void
|
||||
icon?: Component
|
||||
color?: 'brand' | 'red' | 'orange' | 'green' | 'blue' | 'standard'
|
||||
keepOpen?: boolean
|
||||
}
|
||||
@@ -18,6 +21,9 @@ export interface PopupNotificationProgressItem {
|
||||
export interface PopupNotification {
|
||||
id: string | number
|
||||
title: string
|
||||
titleLogo?: Component
|
||||
bodyComponent?: Component
|
||||
bodyProps?: Record<string, unknown>
|
||||
text?: string
|
||||
type?: 'error' | 'warning' | 'success' | 'info' | 'download'
|
||||
progress?: number
|
||||
|
||||
Reference in New Issue
Block a user