Skip to content

Commit

Permalink
bugfix: Fixed non steam games not being added (#45)
Browse files Browse the repository at this point in the history
* Fixed bug preventing non steam games from getting added
* Added copy support for non deck users
CEbbinghaus authored Sep 22, 2024
1 parent 3d16b7e commit 6a1a2d3
Showing 8 changed files with 114 additions and 38 deletions.
85 changes: 63 additions & 22 deletions backend/src/api.rs
Original file line number Diff line number Diff line change
@@ -56,12 +56,16 @@ pub(crate) async fn version() -> impl Responder {
#[get("/health")]
#[instrument]
pub(crate) async fn health() -> impl Responder {
trace!("HTTP GET /health");

HttpResponse::Ok()
}

#[get("/listen")]
#[instrument]
pub(crate) async fn listen(sender: web::Data<Sender<CardEvent>>) -> Result<HttpResponse> {
trace!("HTTP GET /listen");

let event_stream = BroadcastStream::new(sender.subscribe()).map(|res| match res {
Err(_) => Err(Error::from_str("Subscriber Closed")),
Ok(value) => Ok(Event::new(value).into()),
@@ -72,40 +76,48 @@ pub(crate) async fn listen(sender: web::Data<Sender<CardEvent>>) -> Result<HttpR
}

#[get("/list")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn list_cards_with_games(datastore: web::Data<Arc<Store>>) -> impl Responder {
trace!("HTTP GET /list");

web::Json(datastore.list_cards_with_games())
}

#[get("/list/games/{card_id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn list_games_for_card(
card_id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP GET /list/games/{card_id}");

match datastore.get_games_on_card(&card_id) {
Ok(value) => Ok(web::Json(value)),
Err(err) => Err(actix_web::Error::from(err)),
}
}

#[get("/list/cards/{game_id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn list_cards_for_game(
game_id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP GET /list/cards/{game_id}");

match datastore.get_cards_for_game(&game_id) {
Ok(value) => Ok(web::Json(value)),
Err(err) => Err(actix_web::Error::from(err)),
}
}

#[get("/current")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn get_current_card_and_games(
datastore: web::Data<Arc<Store>>,
) -> Result<Either<impl Responder, impl Responder>> {
trace!("HTTP GET /current");

if !is_card_inserted() {
return Ok(Either::Right(
HttpResponseBuilder::new(StatusCode::NO_CONTENT)
@@ -125,8 +137,10 @@ pub(crate) async fn get_current_card_and_games(
}

#[get("/current/card")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn get_current_card(datastore: web::Data<Arc<Store>>) -> Result<impl Responder> {
trace!("HTTP GET /current/card");

if !is_card_inserted() {
return Err(Error::from_str("No card is inserted").into());
}
@@ -139,6 +153,8 @@ pub(crate) async fn get_current_card(datastore: web::Data<Arc<Store>>) -> Result
#[get("/current/id")]
#[instrument]
pub(crate) async fn get_current_card_id() -> Result<impl Responder> {
trace!("HTTP GET /current/id");

if !is_card_inserted() {
return Err(Error::from_str("No card is inserted").into());
}
@@ -147,10 +163,12 @@ pub(crate) async fn get_current_card_id() -> Result<impl Responder> {
}

#[get("/current/games")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn get_games_on_current_card(
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP GET /current/games");

if !is_card_inserted() {
return Err(Error::from_str("No card is inserted").into());
}
@@ -164,13 +182,15 @@ pub(crate) async fn get_games_on_current_card(
}

#[post("/card/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn create_card(
id: web::Path<String>,
body: web::Json<MicroSDCard>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /card/{id}");

if *id != body.uid {
return Err(Error::from_str("uid did not match id provided").into());
}
@@ -185,18 +205,18 @@ pub(crate) async fn create_card(
false => datastore.add_card(id.into_inner(), body.into_inner()),
}

trace!("Sending Updated event");
_ = sender.send(CardEvent::Updated);
Ok(HttpResponse::Ok())
}

#[delete("/card/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn delete_card(
id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP DELETE /card/{id}");
datastore.remove_element(&id)?;

trace!("Sending Updated event");
@@ -205,21 +225,24 @@ pub(crate) async fn delete_card(
}

#[get("/card/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn get_card(
id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP GET /card/{id}");
Ok(web::Json(datastore.get_card(&id)?))
}

#[post("/cards")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn update_cards(
body: web::Json<Vec<MicroSDCard>>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /cards");

for card in body.iter() {
let card = card.to_owned();

@@ -240,18 +263,21 @@ pub(crate) async fn update_cards(
}

#[get("/cards")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn list_cards(datastore: web::Data<Arc<Store>>) -> impl Responder {
trace!("HTTP GET /cards");
web::Json(datastore.list_cards())
}

#[post("/game/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn create_game(
id: web::Path<String>,
body: web::Json<Game>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP POST /game/{id}");

if *id != body.uid {
return Err(Error::from_str("uid did not match id provided").into());
}
@@ -267,38 +293,43 @@ pub(crate) async fn create_game(
}

#[delete("/game/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn delete_game(
id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP DELETE /game/{id}");
datastore.remove_element(&id)?;

Ok(HttpResponse::Ok())
}

#[get("/game/{id}")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn get_game(
id: web::Path<String>,
datastore: web::Data<Arc<Store>>,
) -> Result<impl Responder> {
trace!("HTTP GET /game/{id}");
Ok(web::Json(datastore.get_game(&id)?))
}

#[get("/games")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn list_games(datastore: web::Data<Arc<Store>>) -> impl Responder {
trace!("HTTP GET /games");
web::Json(datastore.list_games())
}

#[allow(clippy::async_yields_async)]
#[post("/games")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn create_games(
body: web::Json<Vec<Game>>,
datastore: web::Data<Arc<Store>>,
) -> impl Responder {
trace!("HTTP POST /games");

for game in body.iter() {
let mut game = game.to_owned();

@@ -325,12 +356,14 @@ pub struct ManyLinkBody {
}

#[post("/link")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn create_link(
body: web::Json<LinkBody>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /link");

datastore.link(&body.game_id, &body.card_id)?;

trace!("Sending Updated event");
@@ -339,12 +372,14 @@ pub(crate) async fn create_link(
}

#[post("/linkmany")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn create_links(
body: web::Json<ManyLinkBody>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /linkmany");

let data = body.into_inner();
for game_id in data.game_ids.iter() {
datastore.link(game_id, &data.card_id)?;
@@ -356,12 +391,14 @@ pub(crate) async fn create_links(
}

#[post("/unlink")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn delete_link(
body: web::Json<LinkBody>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /unlink");

datastore.unlink(&body.game_id, &body.card_id)?;

trace!("Sending Updated event");
@@ -370,12 +407,14 @@ pub(crate) async fn delete_link(
}

#[post("/unlinkmany")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn delete_links(
body: web::Json<ManyLinkBody>,
datastore: web::Data<Arc<Store>>,
sender: web::Data<Sender<CardEvent>>,
) -> Result<impl Responder> {
trace!("HTTP POST /unlinkmany");

let data = body.into_inner();
for game_id in data.game_ids.iter() {
datastore.unlink(game_id, &data.card_id)?;
@@ -387,8 +426,10 @@ pub(crate) async fn delete_links(
}

#[post("/save")]
#[instrument]
#[instrument(skip(datastore))]
pub(crate) async fn save(datastore: web::Data<Arc<Store>>) -> Result<impl Responder> {
trace!("HTTP POST /save");

datastore.write_to_file()?;

Ok(HttpResponse::Ok())
31 changes: 28 additions & 3 deletions backend/src/log.rs
Original file line number Diff line number Diff line change
@@ -2,7 +2,16 @@ use crate::cfg::CONFIG;
use crate::{get_file_path_and_create_directory, LOG_DIR};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::Layer;
use tracing_subscriber::{filter, Layer};

const IGNORED_MODULES: [&'static str; 6] = [
"actix_http::h1::decoder",
"actix_http::h1::dispatcher",
"actix_http::h1::timer",
"actix_server::signals",
"actix_server::worker",
"mio::poll",
];

pub fn create_subscriber() {
let log_file_path = get_file_path_and_create_directory(&CONFIG.log_file, &LOG_DIR)
@@ -19,13 +28,29 @@ pub fn create_subscriber() {
.with_writer(file)
.with_filter(tracing_subscriber::filter::LevelFilter::from_level(
CONFIG.log_level,
));
))
.with_filter(filter::filter_fn(|metadata| {
metadata
.module_path()
.is_some_and(|module| !IGNORED_MODULES.contains(&module))
}));

let subscriber = tracing_subscriber::registry().with(file_writer);

if cfg!(debug_assertions) {
subscriber
.with(tracing_subscriber::fmt::layer().pretty())
.with(
tracing_subscriber::fmt::layer()
.pretty()
.with_filter(tracing_subscriber::filter::LevelFilter::from_level(
CONFIG.log_level,
))
.with_filter(filter::filter_fn(|metadata| {
metadata
.module_path()
.is_some_and(|module| !IGNORED_MODULES.contains(&module))
})),
)
.init();
} else {
subscriber.init();
1 change: 1 addition & 0 deletions backend/src/steam.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(dead_code)]
use std::fmt::{Debug, Display};

use serde::Deserialize;
2 changes: 1 addition & 1 deletion backend/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.10.8
0.10.9
4 changes: 2 additions & 2 deletions lib/src/backend.ts
Original file line number Diff line number Diff line change
@@ -156,10 +156,10 @@ export async function fetchCardsForGame({ url, logger, gameId }: FetchProps & {
}

export async function fetchCreateGame({ url, logger, game}: FetchProps & { game: Game }) {
await wrapFetch({ url: `${url}/game`, logger }, {
await wrapFetch({ url: `${url}/game/${game.uid}`, logger }, {
method: "POST",
...ApplicationJsonHeaders,
body: JSON.stringify({game}),
body: JSON.stringify(game),
});
}

17 changes: 12 additions & 5 deletions src/components/CardActions.tsx
Original file line number Diff line number Diff line change
@@ -30,17 +30,24 @@ export function CardActionsContextMenu({ cardAndGames, currentCard, microSDeck }
<MenuItem onSelected={() => {
showModal(<EditCardModal
onConfirm={async (card: MicroSDCard, nonSteamAdditions: string[], nonSteamDeletions: string[]) => {
microSDeck.updateCard(card);
Logger.Debug("Creating Card");
await microSDeck.updateCard(card);

await Promise.all(nonSteamAdditions.map(appId => {
const appName = collectionStore.deckDesktopApps?.apps.get(parseInt(appId))?.display_name ?? "Unknown Game";
Logger.Debug(`Creating Non-Steam Game ${appId}`);

const appName = collectionStore.deckDesktopApps?.allApps.find(v => v.appid == parseInt(appId))?.display_name ?? "Unknown Game";

return microSDeck.createGame({ uid: appId, name: appName, is_steam: false, size: 0 })
.catch(Error => Logger.Error("There was a critical error creating game: \"{Error}\"", { Error }));
}));

microSDeck.linkMany(card, nonSteamAdditions);


Logger.Debug("Created Non-Steam Games");

Logger.Debug("Linking Many");
await microSDeck.linkMany(card, nonSteamAdditions);

Logger.Debug("Unlinking Many");
await microSDeck.unlinkMany(card, nonSteamDeletions);
}}
card={{ ...card }}
2 changes: 1 addition & 1 deletion src/modals/EditCardModal.tsx
Original file line number Diff line number Diff line change
@@ -129,7 +129,7 @@ interface NonSteamPanelProps {
const NonSteamPanel: VFC<NonSteamPanelProps> = ({ checkedIds, idsOnCard, numAdditions, numDeletions, style, onChange }) => {
const [isOpen, setIsOpen] = useState(false);
const [query, setQuery] = useState("");
const idNamePairs: [number, string][] = useMemo(() => collectionStore.deckDesktopApps ? collectionStore.deckDesktopApps.allApps.map(appOverview => [appOverview.appid, appOverview.display_name]) : [], [collectionStore.deckDesktopApps?.allApps.length]);
const idNamePairs: [number, string][] = useMemo(() => collectionStore.deckDesktopApps ? collectionStore.deckDesktopApps.allApps.map(appOverview => [appOverview.appid, appOverview.display_name]) : [], [...(collectionStore.deckDesktopApps?.allApps.map(v => v.appid) ?? [])]);
const [filteredPairs, setFilteredPairs] = useState(idNamePairs);

useEffect(() => {
10 changes: 6 additions & 4 deletions util/build.mjs
Original file line number Diff line number Diff line change
@@ -128,13 +128,15 @@ if (tasks.includes('collect')) {
copyFileSync('package.json', 'build/package.json');
}

const is_local = existsSync('/home/deck/homebrew');
const current_user = execSync("whoami").toString().trim();

const is_local = existsSync(`/home/${current_user}/homebrew`);

if (is_local && tasks.includes('copy')) {
Logger.Log('Copying build folder to local plugin directory');
execSync(`sudo rm -rf /home/deck/homebrew/plugins/${PluginName}`);
execSync(`sudo cp -r build/ /home/deck/homebrew/plugins/${PluginName}`);
execSync(`sudo chmod 555 /home/deck/homebrew/plugins/${PluginName}`);
execSync(`sudo rm -rf /home/${current_user}/homebrew/plugins/${PluginName}`);
execSync(`sudo cp -r build/ /home/${current_user}/homebrew/plugins/${PluginName}`);
execSync(`sudo chmod 555 /home/${current_user}/homebrew/plugins/${PluginName}`);
} else {
if (!tasks.includes('copy')) {
Logger.Log('Skipping copying build folder to local plugin directory');

0 comments on commit 6a1a2d3

Please sign in to comment.