mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
add frontend billing package, Tauri "get credits command"
This commit is contained in:
1 parent
5360dd0f58
commit
64b91cd6bc
20 files changed
+575
-65
No files matched your search
@@ -40,3 +40,14 @@ impl From<&str> for CommandErrorResponseWrapper<(), ()> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for CommandErrorResponseWrapper<(), ()> {
|
||||
fn from(value: String) -> Self {
|
||||
CommandErrorResponseWrapper {
|
||||
status: CommandErrorStatus::BadRequest, // NB: Default to 500 error.
|
||||
error_message: Some(value),
|
||||
error_type: None,
|
||||
error_details: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod storyteller_open_subscription_purchase_command;
|
||||
pub mod storyteller_open_credits_purchase_command;
|
||||
pub mod storyteller_get_credits_command;
|
||||
pub mod storyteller_open_credits_purchase_command;
|
||||
pub mod storyteller_open_subscription_purchase_command;
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
use crate::core::commands::enqueue::image_edit::enqueue_contextual_edit_image_command::{ContextualImageEditModel, EditImageQuality, EditImageSize};
|
||||
use crate::core::commands::response::shorthand::ResponseOrErrorMessage;
|
||||
use crate::core::state::app_env_configs::app_env_configs::AppEnvConfigs;
|
||||
use crate::core::state::data_dir::app_data_root::AppDataRoot;
|
||||
use crate::core::state::provider_priority::Provider;
|
||||
use crate::services::midjourney::state::midjourney_credential_manager::MidjourneyCredentialManager;
|
||||
use crate::services::midjourney::windows::open_midjourney_login_window::open_midjourney_login_window;
|
||||
use crate::services::sora::windows::sora_login_window::open_sora_login_window::open_sora_login_window;
|
||||
use crate::services::storyteller::state::storyteller_credential_manager::StorytellerCredentialManager;
|
||||
use crate::services::storyteller::windows::open_storyteller_billing_window::{open_storyteller_billing_window, OpenStorytellerBillingWindowArgs, ProductInfo};
|
||||
use artcraft_api_defs::stripe_artcraft::create_subscription_checkout::PlanBillingCadence;
|
||||
use enums::common::artcraft_credits_pack_slug::ArtcraftCreditsPackSlug;
|
||||
use enums::common::artcraft_subscription_slug::ArtcraftSubscriptionSlug;
|
||||
use enums::common::payments_namespace::PaymentsNamespace;
|
||||
use enums::tauri::ux::tauri_command_caller::TauriCommandCaller;
|
||||
use errors::AnyhowResult;
|
||||
use log::{error, info};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use storyteller_client::credits::get_session_credits::get_session_credits;
|
||||
use tauri::{AppHandle, State};
|
||||
use crate::core::commands::providers::get_provider_order_command::GetProviderOrderResponse;
|
||||
use crate::core::commands::response::success_response_wrapper::SerializeMarker;
|
||||
// TODO(bt,2025-09-12): Cache credits and clear cache on generate calls.
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct GetCreditsResponse {
|
||||
pub free_credits: u64,
|
||||
pub monthly_credits: u64,
|
||||
pub banked_credits: u64,
|
||||
pub sum_total_credits: u64,
|
||||
}
|
||||
|
||||
impl SerializeMarker for GetCreditsResponse {}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn storyteller_get_credits_command(
|
||||
app_env_configs: State<'_, AppEnvConfigs>,
|
||||
storyteller_creds_manager: State<'_, StorytellerCredentialManager>,
|
||||
) -> ResponseOrErrorMessage<GetCreditsResponse> {
|
||||
|
||||
info!("storyteller_get_credits_command called");
|
||||
|
||||
let credits = get_credits(
|
||||
&app_env_configs,
|
||||
&storyteller_creds_manager)
|
||||
.await
|
||||
.map_err(|err| {
|
||||
error!("Error getting credits: {:?}", err);
|
||||
format!("Error getting credits: {:?}", err)
|
||||
})?;
|
||||
|
||||
Ok(credits.into())
|
||||
}
|
||||
|
||||
async fn get_credits(
|
||||
app_env_configs: &AppEnvConfigs,
|
||||
storyteller_creds_manager: &StorytellerCredentialManager,
|
||||
) -> AnyhowResult<GetCreditsResponse> {
|
||||
|
||||
let maybe_creds = storyteller_creds_manager.get_credentials()?;
|
||||
|
||||
let response = get_session_credits(
|
||||
&app_env_configs.storyteller_host,
|
||||
maybe_creds.as_ref(),
|
||||
PaymentsNamespace::Artcraft,
|
||||
).await?;
|
||||
|
||||
Ok(GetCreditsResponse {
|
||||
free_credits: response.free_credits,
|
||||
monthly_credits: response.monthly_credits,
|
||||
banked_credits: response.banked_credits,
|
||||
sum_total_credits: response.sum_total_credits,
|
||||
})
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
} from "@fortawesome/pro-regular-svg-icons";
|
||||
import { Button } from "@storyteller/ui-button";
|
||||
import { PopoverMenu } from "@storyteller/ui-popover";
|
||||
import { AuthButtons } from "./AuthButtons";
|
||||
import { SceneTitleInput } from "./SceneTitleInput";
|
||||
import { Activity } from "~/pages/PageEnigma/comps/GenerateModals/Activity";
|
||||
import {
|
||||
@@ -393,6 +392,7 @@ export const TopBar = ({ pageName, loginSignUpPressed }: Props) => {
|
||||
</div>
|
||||
|
||||
<div className="no-drag">
|
||||
{/* TODO(bt,2025-09-12): This was the old auth buttons that didn't work. We need to remove this and clean up the DOM. */}
|
||||
</div>
|
||||
|
||||
{isDesktop && platform !== "macos" && (
|
||||
|
||||
@@ -91,6 +91,9 @@
|
||||
{
|
||||
"path": "../../libs/common"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/billing"
|
||||
},
|
||||
{
|
||||
"path": "../../libs/analytics/google-analytics"
|
||||
},
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nx/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# @frontend/billing
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test @frontend/billing` to execute the unit tests via [Vitest](https://vitest.dev/).
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/billing",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
export * from './lib/billing';
|
||||
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* Replace this with your own classes
|
||||
*
|
||||
* e.g.
|
||||
* .container {
|
||||
* }
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
import FrontendBilling from './billing';
|
||||
|
||||
describe('FrontendBilling', () => {
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<FrontendBilling />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
import styles from './billing.module.css';
|
||||
|
||||
export function FrontendBilling() {
|
||||
return (
|
||||
<div className={styles['container']}>
|
||||
<h1>Welcome to FrontendBilling!</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default FrontendBilling;
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../tauri-api"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-utils"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-events"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../tauri-api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-utils/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../tauri-events/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../node_modules/.vite/libs/billing',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
// Uncomment this if you are using workers.
|
||||
// worker: {
|
||||
// plugins: [ nxViteTsPaths() ],
|
||||
// },
|
||||
// Configuration for building your library.
|
||||
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
// Could also be a dictionary or array of multiple entry points.
|
||||
entry: 'src/index.ts',
|
||||
name: '@frontend/billing',
|
||||
fileName: 'index',
|
||||
// Change this to the formats you want to support.
|
||||
// Don't forget to update your package.json as well.
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
// External packages that should not be bundled into your library.
|
||||
external: ['react','react-dom','react/jsx-runtime']
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./lib/artcraft/ArtcraftGetCredits.js";
|
||||
export * from "./lib/common/CommandStatus.js";
|
||||
export * from "./lib/enqueue/EnqueueContextualEditImage.js";
|
||||
export * from "./lib/enqueue/EnqueueImageBgRemovalCommand.js";
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { CommandResult } from "../common/CommandStatus";
|
||||
|
||||
export interface ArtcraftGetCreditsSuccess extends CommandResult {
|
||||
payload: ArtcraftGetCreditsPayload;
|
||||
}
|
||||
|
||||
export interface ArtcraftGetCreditsPayload {
|
||||
free_credits: number,
|
||||
monthly_credits: number,
|
||||
banked_credits: number,
|
||||
sum_total_credits: number,
|
||||
}
|
||||
|
||||
// Returns the Success and Error variants directly.
|
||||
// Throws on Network/Tauri errors.
|
||||
export const ArtcraftGetCredits = async () : Promise<ArtcraftGetCreditsSuccess> => {
|
||||
try {
|
||||
return await invoke("storyteller_get_credits_command") as ArtcraftGetCreditsSuccess;
|
||||
} catch (error) {
|
||||
// NB: Endpoint should be infalliable
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
Generated
+222
-62
File diff suppressed because it is too large.
Load diff
@@ -161,6 +161,9 @@
|
||||
},
|
||||
{
|
||||
"path": "./libs/components/provider-billing-modal"
|
||||
},
|
||||
{
|
||||
"path": "./libs/billing"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user