add firebase

main
reng 4 months ago
parent db8407c1c3
commit 561564a034
  1. 1063
      package-lock.json
  2. 1
      package.json
  3. 4
      src-tauri/2
  4. 19
      src-tauri/capabilities/default.json
  5. 103
      src-tauri/src/lib.rs
  6. 4
      src-tauri/src/raw_convert.rs
  7. 4
      src-tauri/src/util.rs
  8. 9
      src-tauri/tauri.conf.json
  9. 13
      src/App.css
  10. 140
      src/App.jsx
  11. 77
      src/utils/backend.js
  12. 29
      src/utils/fs.js

1063
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -15,6 +15,7 @@
"@tauri-apps/plugin-dialog": "^2.3.2",
"@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-opener": "^2",
"firebase": "^12.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},

@ -1,7 +1,7 @@
added 1 package, and audited 170 packages in 4s
up to date, audited 256 packages in 4s
25 packages are looking for funding
28 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities

@ -9,6 +9,23 @@
"core:default",
"opener:default",
"dialog:default",
"fs:default"
"fs:write-files",
"fs:allow-create",
"fs:allow-appdata-write",
"fs:allow-appdata-read",
"fs:allow-exists",
{
"identifier": "fs:scope",
"allow": [
{"path":"$DOCUMENT/**/*"},
{"path":"$DOCUMENT"},
{
"path": "$APPDATA"
},
{
"path": "$APPDATA/**/*"
}
]
}
]
}

@ -131,14 +131,18 @@ fn default_ticket(quality: Option<String>) -> PrintTicket {
fn print_document(
device_name: String,
document_path: String,
dpi: String,
color: String,
size: String,
dpi: Option<String>,
color: Option<String>,
size: Option<String>,
quality: Option<String>,
) {
println!(
"Printing document: {}, {}, {}, {}, {}",
document_path, size, dpi, color, quality.clone().unwrap_or_else(|| "none".to_string())
"Printing document: {}, size: {}, dpi: {}, color: {}, quality: {}",
document_path,
size.clone().unwrap_or_else(|| "none".to_string()),
dpi.clone().unwrap_or_else(|| "none".to_string()),
color.clone().unwrap_or_else(|| "none".to_string()),
quality.clone().unwrap_or_else(|| "none".to_string())
);
let path = Path::new(&document_path);
@ -178,58 +182,67 @@ fn print_document(
let capabilities = PrintCapabilities::fetch(&my_device).unwrap();
let mut builder = PrintTicketBuilder::new(&my_device).unwrap();
// size
let parsed_size = size
.parse::<PredefinedMediaName>()
.expect("Invalid media size name");
let a6_media = capabilities
.page_media_sizes()
.find(|x| x.as_predefined_name() == Some(parsed_size))
.expect("Media size not supported by printer");
if let Some(size) = &size {
let parsed_size = size
.parse::<PredefinedMediaName>()
.expect("Invalid media size name");
let media = capabilities
.page_media_sizes()
.find(|x| x.as_predefined_name() == Some(parsed_size))
.expect("Media size not supported by printer");
builder.merge(media).unwrap();
}
// dpi
let parsed_dpi = dpi
.split('x')
.next()
.and_then(|v| v.parse::<u32>().ok())
.expect("Invalid DPI format");
let supported_dpi = capabilities
.page_resolutions()
.find(|x| x.dpi() == (parsed_dpi, parsed_dpi))
.expect("DPI not supported by printer");
// dpi
if let Some(dpi) = &dpi {
let parsed_dpi = dpi
.split('x')
.next()
.and_then(|v| v.parse::<u32>().ok())
.expect("Invalid DPI format");
let supported_dpi = capabilities
.page_resolutions()
.find(|x| x.dpi() == (parsed_dpi, parsed_dpi))
.expect("DPI not supported by printer");
builder.merge(supported_dpi).unwrap();
}
// color
let parsed_color = capabilities
.page_output_colors()
.find(|x| {
x.as_predefined_name() == Some(color.parse::<PredefinedPageOutputColor>().unwrap())
})
.map(|x| x.as_predefined_name().unwrap())
.expect("Invalid color name");
let supported_color = capabilities
.page_output_colors()
.find(|x| x.as_predefined_name() == Some(parsed_color))
.expect("Color not supported by printer");
let mut builder = PrintTicketBuilder::new(&my_device).unwrap();
if let Some(color) = &color {
let parsed_color = capabilities
.page_output_colors()
.find(|x| {
x.as_predefined_name() == Some(color.parse::<PredefinedPageOutputColor>().unwrap())
})
.map(|x| x.as_predefined_name().unwrap())
.expect("Invalid color name");
let supported_color = capabilities
.page_output_colors()
.find(|x| x.as_predefined_name() == Some(parsed_color))
.expect("Color not supported by printer");
builder.merge(supported_color).unwrap();
let default_ticket = default_ticket(quality);
if let Err(e) = builder.merge(default_ticket) {
println!("Failed to merge default ticket: {:?}", e);
}
builder.merge(a6_media).unwrap();
builder.merge(supported_dpi).unwrap();
builder.merge(supported_color).unwrap();
// let default_ticket = default_ticket(quality);
// if let Err(e) = builder.merge(default_ticket) {
// println!("Failed to merge default ticket: {:?}", e);
// }
let ticket = builder.build().unwrap();
let xml=ticket.get_xml();
let xml_string = String::from_utf8_lossy(&xml).to_string();
println!("PrintTicket XML as String: {}", xml_string);
// let xml=ticket.get_xml();
// let xml_string = String::from_utf8_lossy(&xml).to_string();
// println!("PrintTicket XML as String: {}", xml_string);
return;
let path = Path::new(&document_path);

@ -43,8 +43,8 @@ pub fn img2pdf_from_bytes(img_data: &[u8], dpi: f64) -> io::Result<Vec<u8>> {
((height as f64) / dpi * 72.0).round() as u32,
);
println!("Image size in pixels: {}x{}", width, height);
println!("Dest Image size in points: {}x{}", page_width, page_height);
// println!("Image size in pixels: {}x{}", width, height);
// println!("Dest Image size in points: {}x{}", page_width, page_height);
let (rgb_img, mask_img) = separate_rgb_and_alpha(img);

@ -80,8 +80,8 @@ pub fn get_output_quality_options(xml: &str) -> Vec<String> {
// let mut buf = Vec::new();
let mut inside_target_feature = false;
let mut inside_option = false;
let mut current_option_name = String::new();
// let mut inside_option = false;
// let mut current_option_name = String::new();
let mut results = HashSet::new();
loop {

@ -13,8 +13,8 @@
"windows": [
{
"title": "printer",
"width": 400,
"height": 560
"width": 700,
"height": 450
}
],
"security": {
@ -31,5 +31,10 @@
"icons/icon.icns",
"icons/icon.ico"
]
},
"plugins": {
"fs": {
"requireLiteralLeadingDot": false
}
}
}

@ -1,10 +1,17 @@
@import "tailwindcss";
main {
@apply flex flex-col items-center justify-start min-h-screen gap-4 p-4;
html{
}
main{
@apply w-full min-h-screen;
}
section {
@apply flex flex-col items-center justify-center gap-4 p-4;
@apply bg-gray-100 rounded-xl;
}
button{
@apply px-4 py-2 bg-fuchsia-400 text-black font-bold rounded hover:bg-fuchsia-500;
@apply px-4 py-2 bg-fuchsia-400 text-black font-bold rounded-full hover:bg-fuchsia-500;
}
select, input[type="file"] {
@apply px-4 py-2 border border-gray-300 rounded;

@ -1,9 +1,10 @@
import { invoke } from "@tauri-apps/api/core";
import { open } from '@tauri-apps/plugin-dialog';
import { open, save } from '@tauri-apps/plugin-dialog';
import "./App.css";
import { useEffect, useState } from "react";
import { AvailableQualities } from "./params";
import { listenToPrints, clearPrints, deletePrint, createTestFile } from "./utils/backend";
import { saveToDisk } from "./utils/fs";
const OptionTypes=[
'dpi_options',
@ -11,11 +12,16 @@ const OptionTypes=[
'size_options',
'quality_options',
]
const ENABLE_OPTIONS=false;
function App() {
const [printer, setPrinter] = useState("");
const [selectedFile, setSelectedFile] = useState();
const [playing, setPlaying] = useState(false);
const [printing, setPrinting] = useState();
const [prints, setPrints] = useState([]);
function getPrinters() {
invoke("get_all_printers")
@ -76,14 +82,21 @@ function App() {
return;
}
invoke("print_document",{
let options = {
deviceName: printer, // Use the selected printer
documentPath: file, // Use the selected file path
dpi: document.getElementById("dpi_options").value,
color: document.getElementById("color_options").value,
size: document.getElementById("size_options").value,
quality: document.getElementById("quality_options").value,
})
};
if(ENABLE_OPTIONS) {
options = {
...options,
dpi: document.getElementById("dpi_options").value,
color: document.getElementById("color_options").value,
size: document.getElementById("size_options").value,
quality: document.getElementById("quality_options").value,
};
}
invoke("print_document", options)
.then(() => {
console.log("Print job sent successfully.");
})
@ -104,9 +117,53 @@ function App() {
}
});
}
function printLatest(){
if(prints.length === 0) {
console.log("No prints available to process.");
return;
}
const latestPrint = prints[prints.length - 1];
console.log("Latest print job:", latestPrint);
setPrinting(latestPrint.id);
// save url to local disk
saveToDisk(latestPrint.id, latestPrint.url)
.then((filePath) => {
console.log("File saved to:", filePath);
invoke("print_document", {
deviceName: printer, // Use the printer from the latest print job
documentPath: filePath, // Use the document path from the latest print job
}).then(() => {
console.log("Print job sent successfully for latest print.");
// TODO: clear print records
deletePrint(latestPrint.id); // Clear the print record after sending the job
console.log("Print record deleted:", latestPrint.id);
setPrinting();
}).catch((error) => {
console.error("Error sending print job for latest print:", error);
});
}).catch((error) => {
console.error("Error saving file:", error);
});
}
useEffect(()=>{
if(!printer) return;
if(!ENABLE_OPTIONS) return;
invoke("get_printer_capabilities", { deviceName: printer })
.then((capabilities) => {
console.log(`Capabilities for ${printer}:`, capabilities);
@ -137,28 +194,67 @@ function App() {
},[printer]);
useEffect(() => {
if (!playing) return;
printLatest();
}, [prints, playing]);
useEffect(() => {
getPrinters();
listenToPrints((newPrints) => {
console.log("Received prints:", newPrints);
setPrints(newPrints);
});
}, []);
return (
<main className="container *:w-full">
<button className="!w-auto self-start !bg-fuchsia-200" onClick={getPrinters}>refresh printers</button>
<select id="printer-select" value={printer} onChange={(e) => setPrinter(e.target.value)}></select>
<select id="size_options"></select>
<select id="dpi_options"></select>
<select id="color_options"></select>
<select id="quality_options"></select>
<div className="flex flex-row gap-2">
<label id="document_path_label" className="flex-1 break-all">{selectedFile ? `${selectedFile}` : "Select a document to print:"}</label>
<button onClick={openFile}>open</button>
</div>
<main className="grid grid-cols-2 gap-2 p-2">
<div className="grid grid-rows-2 gap-2">
<section className="*:w-full">
<button className="!w-auto self-start !bg-amber-200" onClick={getPrinters}>refresh printers</button>
<select id="printer-select" value={printer} onChange={(e) => setPrinter(e.target.value)}></select>
{ENABLE_OPTIONS && <>
<select id="size_options"></select>
<select id="dpi_options"></select>
<select id="color_options"></select>
<select id="quality_options"></select>
</>}
</section>
<section className="*:w-full">
<div className="flex flex-row gap-2 items-center">
<label id="document_path_label" className="flex-1 break-all">{selectedFile ? `${selectedFile}` : "Select a document to print:"}</label>
<button onClick={openFile}>open</button>
</div>
<label id="pdf_path_label" className=""></label>
<button onClick={convertPdf}>convert PDF</button>
<label id="pdf_path_label" className="flex-1"></label>
<div className="grid grid-cols-2 gap-2">
<button onClick={convertPdf}>convert PDF</button>
<button onClick={printDocument}>print</button>
</div>
</section>
</div>
<section className="*:w-full">
<div className="border flex-1 p-2 overflow-y-auto overflow-x-hidden text-sm break-all flex flex-col gap-1">
{prints.length > 0 ? (
prints.map((print) => (
<div key={print.id} className="bg-green-200 px-2 rounded-full">{print.id}</div>
))
) : (
<p>No prints available</p>
)}
</div>
<div className="grid grid-cols-2 gap-2">
<button onClick={()=>setPlaying(!playing)} className={playing? '!bg-green-300':''}>{playing? 'pause':'play'}</button>
<button onClick={clearPrints}>clear all</button>
<button onClick={createTestFile}>create test file</button>
</div>
<p className="h-[4rem]">{printing? `Printing document with id ${printing}` : "No document is being printed."}</p>
<button onClick={printDocument}>print</button>
</section>
</main>
);
}

@ -0,0 +1,77 @@
import { initializeApp } from "firebase/app";
import { getFirestore, collection, doc, deleteDoc, onSnapshot, setDoc } from "firebase/firestore";
const firebaseConfig = {
apiKey: "AIzaSyD1VzUKXt0JskwyfjfIAbdROzPNB3fTIw0",
authDomain: "uc-24070-thegreattipsy.firebaseapp.com",
projectId: "uc-24070-thegreattipsy",
storageBucket: "uc-24070-thegreattipsy.firebasestorage.app",
messagingSenderId: "772804793020",
appId: "1:772804793020:web:258003100900c20e0fb6b9",
measurementId: "G-1CRHMJY4L9"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const CollectionName="prints";
export function listenToPrints(callback) {
// Listen for changes in the prints collection
const db = getFirestore(app);
const printsRef = collection(db, CollectionName);
onSnapshot(printsRef, (snapshot) => {
const prints = [];
snapshot.forEach((doc) => {
prints.push({ id: doc.id, ...doc.data() });
});
callback(prints);
});
}
export function clearPrints() {
// Clear the prints collection
const db = getFirestore(app);
const printsRef = collection(db, CollectionName);
onSnapshot(printsRef, (snapshot) => {
snapshot.forEach((doc) => {
// Assuming you have a delete function to remove documents
deleteDoc(doc.ref);
});
});
}
export function deletePrint(id) {
// Delete a specific print by id
const db = getFirestore(app);
const printRef = doc(collection(db, CollectionName), id);
deleteDoc(printRef)
.then(() => {
console.log(`Print with id ${id} deleted successfully.`);
})
.catch((error) => {
console.error(`Error deleting print with id ${id}:`, error);
});
}
export function createTestFile(){
// Create a test print file
const db = getFirestore(app);
const testPrint = {
url: "https://s3.ap-northeast-2.amazonaws.com/ultracombos.project/24070-%E5%BE%AE%E9%86%BA%E5%A4%A7%E9%A3%AF%E5%BA%97%E9%AB%98%E9%9B%84%E7%89%88/Postcard-01.png",
createdAt: new Date().toISOString(),
id: `test-${Date.now()}`,
status: "pending"
};
// Add the test print to the collection
setDoc(doc(db, CollectionName, testPrint.id), testPrint)
.then(() => {
console.log("Test print created successfully.");
})
.catch((error) => {
console.error("Error creating test print:", error);
});
}

@ -0,0 +1,29 @@
import { writeFile, BaseDirectory, exists, mkdir } from '@tauri-apps/plugin-fs';
import { path } from '@tauri-apps/api';
const OutputDir = 'prints';
export async function saveToDisk(id, url){
console.log("Saving file to disk:", id, url);
const response = await fetch(url);
const arrayBuffer = await response.arrayBuffer();
const base=BaseDirectory.Document;
const folder=`${OutputDir}`;
const filePath = `${folder}/${id}.png`;
// Check if the directory exists, if not create it
const dirExists = await exists(folder, { baseDir: base });
if (!dirExists) {
console.log("Directory does not exist, creating:", folder);
await mkdir(folder, { baseDir: base });
}
// Write the file to the disk
await writeFile(filePath, arrayBuffer, { baseDir: base });
return `${await path.documentDir()}\\${filePath}`;
}
Loading…
Cancel
Save