Compare commits

...
Author SHA1 Message Date
ArEnSc 9c0d9eaa11 Merge branch 'main' into feature/ui-sign-in 2025-04-27 21:52:59 -04:00
Michael Chung 532513ad15 Gravatar profile image fixes and user info fixes 2025-04-25 10:00:36 -04:00
Michael Chung 1e42cf1dfa Using the new api manager to login
Fixing the login page
using the router instead of remix links.
2025-04-23 22:27:25 -04:00
Michael Chung 757baf02f0 Modified the sign in and sign up buttons to use the new button link component 2025-04-23 16:33:49 -04:00
Michael Chung e9299ef97d ui components 2025-04-22 23:51:58 -04:00
19 changed files with 514 additions and 252 deletions
@@ -63,6 +63,7 @@ export class UsersApi extends ApiManager {
password: string;
}): Promise<ApiResponse<{ signedSession?: string }>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/login`;
const body = {
username_or_email: usernameOrEmail,
password: password,
+22 -4
View File
@@ -5,10 +5,11 @@ import { faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
import { Button, Input, LoadingSpinner } from "~/components/ui";
import { authentication } from "~/signals";
import { twMerge } from "tailwind-merge";
import { UsersApi } from "@storyteller/api";
export const Login = () => {
const {
signals: { status: authStatus },
signals: { status: authStatus, userInfo },
fetchers: { login },
enums: { AUTH_STATUS },
} = authentication;
@@ -36,17 +37,33 @@ export const Login = () => {
return "Getting Session...";
}
const handleOnSumbit = (ev: React.FormEvent<HTMLFormElement>) => {
const handleOnSumbit = async (ev: React.FormEvent<HTMLFormElement>) => {
ev.preventDefault();
if (formRef.current) {
const form = new FormData(formRef.current);
const usernameOrEmail = form.get("usernameOrEmail")?.toString();
const password = form.get("password")?.toString();
if (usernameOrEmail && password && login) {
login({
if (usernameOrEmail && password) {
let api = new UsersApi();
let response = await api.Login({
usernameOrEmail,
password,
});
if (response.success) {
let session = response.data?.signedSession;
if (session) {
localStorage.setItem("session", session);
authStatus.value = AUTH_STATUS.LOGGED_IN;
let response = await api.GetUserProfile(usernameOrEmail);
if (response.success) {
userInfo.value = response.data?.user;
}
}
}
}
}
}; // end handleOnSubmit
@@ -82,6 +99,7 @@ export const Login = () => {
shouldShowLoader && "opacity-0",
)}
>
<Input
label="Username or Email"
icon={faUser}
@@ -14,6 +14,7 @@ import { EditModeRootComponent } from "~/EditModeRootComponent/EditModeRootCompo
import { useRef } from "react";
import { GenerationEngine } from "~/KonvaApp/GenerationEngine";
import { GalleryRootComponent } from "~/GalleryRootComponent";
import { DemoModal } from "@storyteller/ui-demo-modal";
export const isTauri = (): boolean => {
@@ -1,10 +1,12 @@
import { faGear, faImages } from "@fortawesome/pro-solid-svg-icons";
import { twMerge } from "tailwind-merge";
import { Activity } from "~/components/ui/Activity/Activity";
import { GalleryModal } from "@storyteller/ui-gallery-modal";
import { Button } from "@storyteller/ui-button";
import { SettingsModal } from "@storyteller/ui-settings-modal";
import { useState } from "react";
import { AuthButtons } from "~/components/shared_authentication/AuthButtons";
export const ToolbarTopRight = () => {
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
@@ -29,8 +31,8 @@ export const ToolbarTopRight = () => {
My Gallery
</Button>
<Activity />
<AuthButtons/>
</div>
<SettingsModal
isOpen={isSettingsModalOpen}
onClose={() => setIsSettingsModalOpen(false)}
@@ -23,22 +23,7 @@ export function ProfileDropdown() {
} = authentication;
if (!userInfo.value) return null;
// Extract user info for better readability
const {
core_info: {
username,
gravatar_hash: emailHash,
display_name,
default_avatar: {
image_index: avatarIndex,
color_index: backgroundColorIndex,
},
},
} = userInfo.value;
const profileUrl = `https://storyteller.ai/profile/${display_name}`;
const profileUrl = `https://storyteller.ai/profile/${userInfo.value.display_name}`;
const menuOptions = [
{
label: "Logout",
@@ -57,10 +42,10 @@ export function ProfileDropdown() {
>
<Gravatar
size={34}
username={username}
email_hash={emailHash}
avatarIndex={avatarIndex}
backgroundIndex={backgroundColorIndex}
username={userInfo.value.display_name}
email_hash={userInfo.value.email_gravatar_hash}
avatarIndex={Number(userInfo.value.core_info.default_avatar.image_index) || 0}
backgroundIndex={Number(userInfo.value.core_info.default_avatar.color_index) || 0}
/>
<FontAwesomeIcon icon={faChevronDown} />
</MenuButton>
@@ -1,11 +1,15 @@
import { Link, LinkProps } from "@remix-run/react";
import React from "react";
import { useNavigate } from "react-router-dom";
import { IconDefinition } from "@fortawesome/fontawesome-svg-core";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
interface LinkButtonProps extends LinkProps {
interface ButtonLinkProps {
icon?: IconDefinition;
variant?: "primary" | "secondary";
className?: string;
to: string;
children: React.ReactNode;
}
export const ButtonLink = ({
@@ -13,9 +17,11 @@ export const ButtonLink = ({
children,
className: propsClassName,
variant = "primary",
to,
...rest
}: LinkButtonProps) => {
//TODO: Duplicated from Button.tsx
}: ButtonLinkProps) => {
const navigate = useNavigate();
function getVariantClassNames(variant: string) {
switch (variant) {
case "secondary": {
@@ -31,14 +37,19 @@ export const ButtonLink = ({
"text-sm font-medium whitespace-nowrap rounded-lg px-3 py-2 shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 transition-all duration-150";
const variantClassNames = getVariantClassNames(variant);
const className = twMerge(baseClassName, variantClassNames, propsClassName);
// END TODO
const handleClick = () => {
navigate(to);
};
return (
<Link {...rest}>
<button className={className}>
{icon && <FontAwesomeIcon className="mr-2" icon={icon} size="sm" />}
{children}
</button>
</Link>
<button
className={className}
onClick={handleClick}
{...rest}
>
{icon && <FontAwesomeIcon className="mr-2" icon={icon} size="sm" />}
{children}
</button>
);
};
@@ -0,0 +1,38 @@
import { useSignals } from "@preact/signals-react/runtime";
import { AUTH_STATUS } from "~/signals/authentication/enums";
import { authentication } from "~/signals";
import { ButtonLink } from "~/components/reusable/ButtonLink/ButtonLink";
import { ProfileDropdown } from "../features/ToolbarUserProfile/ProfileDropdown";
// TODO MAKE THIS A SHARED COMPONENT ART CRAFT
export const AuthButtons = () => {
useSignals();
const { status } = authentication.signals;
console.log("status", status.value);
if (status.value === AUTH_STATUS.LOGGED_IN) {
return <ProfileDropdown />;
} else {
return (
<div className="flex items-center gap-3.5">
<span className="text-white/20">|</span>
<div className="flex items-center gap-2">
<ButtonLink
to="/login"
variant="primary"
className="bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90" // TODO(bt,2025-04-19): Once we have in-page routing, get rid of this.
>
Login
</ButtonLink>
<ButtonLink
to="/signup"
variant="primary"
className="bg-[#5F5F68]/60 backdrop-blur-lg hover:bg-[#5F5F68]/90" // TODO(bt,2025-04-19): Once we have in-page routing, get rid of this.
>
Sign Up
</ButtonLink>
</div>
</div>
);
}
};
@@ -0,0 +1,193 @@
// import { faEnvelope, faKey, faUser } from "@fortawesome/pro-solid-svg-icons";
// // import { GlobalSettingsManager } from "./pages/PageEnigma/GlobalSettingsManager";
// import { Button } from "@storyteller/ui-button";
// import { Input } from "@storyteller/ui-input";
// import { Toaster } from "@storyteller/ui-toaster";
// // import { authentication, signUp } from "~/signals";
// import { FormEvent, useRef, useState, StrictMode } from "react";
// // import { AUTH_STATUS, ToastTypes } from "~/enums";
// import { useSignalEffect, useSignals } from "@preact/signals-react/runtime";
// import { useNavigate, useSearchParams, BrowserRouter } from "react-router-dom";
// import { createRoot } from "react-dom/client";
// // import "./styles/normalize.css";
// // import "./styles/tailwind.css";
// // import "./styles/base.css";
// import "@fortawesome/fontawesome-svg-core/styles.css";
// import { config } from "@fortawesome/fontawesome-svg-core";
// config.autoAddCss = false; /* eslint-disable import/first */
// // TODO MAKE THIS A SHARED COMPONENT ART CRAFT
// const regEx =
// /^(([^<>()[\].,;:\s@"]+(.[^<>()[\].,;:\s@"]+)*)|(".+"))@(([^<>()[\].,;:\s@"]+\.)+[^<>()[\].,;:\s@"]{2,})$/i;
// // TODO(bt,2025-04-19): Make these configurable
// const ENV = {
// BASE_API: "https://api.storyteller.ai",
// GOOGLE_API: "https://studio.storyteller.ai",
// FUNNEL_API: "https://studio.storyteller.ai",
// CDN_API: "https://cdn-2.fakeyou.com",
// GRAVATAR_API: "https://studio.storyteller.ai",
// DEPLOY_PRIME_URL: "https://studio.storyteller.ai",
// };
// export default function SignUpScreen() {
// useSignals();
// const navigate = useNavigate();
// const formRef = useRef<HTMLFormElement | null>(null);
// const [usernameError, setUsernameError] = useState("");
// const [emailError, setEmailError] = useState("");
// const [passwordError, setPasswordError] = useState("");
// const [passwordConfirmationError, setPasswordConfirmationError] =
// useState("");
// const [searchParams] = useSearchParams();
// // const { status: authStatus } = authentication;
// const handleOnSubmit = (ev: FormEvent<HTMLFormElement>) => {
// ev.preventDefault();
// if (formRef.current) {
// const form = new FormData(formRef.current);
// const email = form.get("email")?.toString();
// const password = form.get("password")?.toString();
// const passwordConfirmation = form
// .get("password-confirmation")
// ?.toString();
// const username = form.get("username")?.toString();
// setUsernameError("");
// if (!username || username.length < 6) {
// setUsernameError("Username must be at least 6 characters long");
// }
// setEmailError("");
// if (!email || !email.match(regEx)) {
// setEmailError("Email must be a valid email address");
// }
// setPasswordError("");
// if (!password || password.length < 8) {
// setPasswordError("Password must be at least 8 characters long");
// }
// setPasswordConfirmationError("");
// if (!passwordError && passwordConfirmation !== password) {
// setPasswordConfirmationError("Passwords do not match");
// }
// if (
// !usernameError &&
// !passwordError &&
// !emailError &&
// !passwordConfirmationError &&
// signUp
// ) {
// signUp({
// username: username!,
// email: email!,
// password: password!,
// passwordConfirmation: passwordConfirmation!,
// }).then((error) => {
// console.log("Error occured", error);
// if (error) {
// Toaster.toast(ToastTypes.ERROR, error);
// }
// });
// }
// }
// }; // end handleOnSubmit
// useSignalEffect(() => {
// const redirectPath = searchParams.get("redirect");
// if (authStatus.value === AUTH_STATUS.LOGGED_IN) {
// navigate(redirectPath ? redirectPath : "/");
// return;
// }
// if (authStatus.value === AUTH_STATUS.NO_ACCESS) {
// window.open("https://storyteller.ai/", "_self");
// return;
// }
// });
// return (
// <div className="fixed flex h-full w-full flex-col items-center justify-center overflow-auto bg-[#1b1b1f] bg-center bg-repeat">
// <div className="mb-7 flex w-10/12 max-w-2xl items-center justify-center gap-4">
// <img
// src="/resources/images/artcraft-logo-3.png"
// alt="ArtCraft Logo"
// className="h-9 select-none"
// />
// </div>
// <div className="w-full max-w-xl rounded-xl shadow-lg">
// <div className="glass glass-no-hover relative mx-auto w-full rounded-none rounded-t-xl p-6 shadow-none">
// <h1 className="mb-8 text-center text-2xl font-bold">Sign up</h1>
// <form
// ref={formRef}
// onSubmit={handleOnSubmit}
// className="flex flex-col gap-4"
// >
// <Input
// label="Username"
// icon={faUser}
// placeholder="Username"
// name="username"
// errorMessage={usernameError}
// required
// />
// <Input
// type="email"
// label="Email"
// icon={faEnvelope}
// placeholder="Email"
// name="email"
// errorMessage={emailError}
// required
// />
// <Input
// type="password"
// label="Password"
// icon={faKey}
// name="password"
// placeholder="Password"
// autoComplete="current-password"
// errorMessage={passwordError}
// required
// />
// <Input
// type="password"
// label="Password Confirmation"
// icon={faKey}
// placeholder="Password Confirmation"
// name="password-confirmation"
// errorMessage={passwordConfirmationError}
// required
// />
// <Button className="mt-4 py-3">Sign up</Button>
// </form>
// </div>
// <div className="align-items flex w-full max-w-xl rounded-b-xl bg-ui-controls px-6 py-4">
// <div className="flex w-full justify-end gap-1 text-sm">
// <p className="opacity-75">Already have an account?</p>
// <a
// href="/login"
// className="text-brand-primary-400 hover:text-brand-primary-300 font-medium"
// >
// Log in
// </a>
// </div>
// </div>
// </div>
// </div>
// );
// }
// // TODO: Replace environment variables from `root.tsx`
// createRoot(document.getElementById("root")!).render(
// <>
// <StrictMode>
// <BrowserRouter>
// {/* <GlobalSettingsManager env={ENV} /> */}
// <SignUpScreen />
// </BrowserRouter>
// </StrictMode>
// </>,
// );
@@ -1,5 +1,4 @@
import { computed, signal } from "@preact/signals-react";
import { UserInfo } from "@ApiManager/models/Users";
import { ActiveSubscriptions } from "@ApiManager/models/Billing";
import { LoyaltyProgram } from "@ApiManager/enums/Billing";
@@ -1,5 +1,5 @@
import { signals } from "./signals";
import { UsersApi } from "@ApiManager/UsersApi";
import { signals } from "./auth_signals";
import { UsersApi } from "@storyteller/api";
import { BillingApi } from "@ApiManager/BillingApi";
import {
@@ -1,4 +1,4 @@
import { signals } from "./signals";
import { signals } from "./auth_signals";
import * as fetchers from "./fetchers";
import * as enums from "./enums";
@@ -2,7 +2,7 @@ import { AUTH_STATUS } from "./enums";
import { UserInfo } from "~/Classes/ApiManager/models/Users";
import { ActiveSubscriptions } from "@ApiManager/models/Billing";
import { signals } from "./signals";
import { signals } from "./auth_signals";
export const updateAuthStatus = (newStatus: AUTH_STATUS) => {
signals.status.value = newStatus;
+3
View File
@@ -42,6 +42,9 @@
},
{
"path": "../../libs/components/demo-modal/tsconfig.lib.json"
},
{
"path": "../../libs/api/tsconfig.lib.json"
}
]
}
+3
View File
@@ -16,6 +16,9 @@
{
"path": "../../libs/components/demo-modal"
},
{
"path": "../../libs/api"
},
{
"path": "./tsconfig.app.json"
},
+84 -84
View File
@@ -1,91 +1,91 @@
import { ApiManager, ApiResponse } from "./ApiManager";
// import { ApiManager, ApiResponse } from "../apis/ApiManager";
export class CommentsApi extends ApiManager {
public CreateComment({
commentMarkdown,
entityToken,
entityType,
uuidIdempotencyToken,
}: {
commentMarkdown: string;
entityToken: string;
entityType: string;
uuidIdempotencyToken: string;
}): Promise<ApiResponse<string>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/new`;
// export class CommentsApi extends ApiManager {
// public CreateComment({
// commentMarkdown,
// entityToken,
// entityType,
// uuidIdempotencyToken,
// }: {
// commentMarkdown: string;
// entityToken: string;
// entityType: string;
// uuidIdempotencyToken: string;
// }): Promise<ApiResponse<string>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/new`;
const body = {
comment_markdown: commentMarkdown,
entity_token: entityToken,
entity_type: entityType,
uuid_idempotency_token: uuidIdempotencyToken,
};
// const body = {
// comment_markdown: commentMarkdown,
// entity_token: entityToken,
// entity_type: entityType,
// uuid_idempotency_token: uuidIdempotencyToken,
// };
return this.post<
{
comment_markdown: string;
entity_token: string;
entity_type: string;
uuid_idempotency_token: string;
},
{
success?: boolean;
comment_token?: string;
BadInput?: string;
}
>({ endpoint, body })
.then((response) => ({
success: response.success ?? false,
data: response.comment_token,
errorMessage: response.BadInput,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
// return this.post<
// {
// comment_markdown: string;
// entity_token: string;
// entity_type: string;
// uuid_idempotency_token: string;
// },
// {
// success?: boolean;
// comment_token?: string;
// BadInput?: string;
// }
// >({ endpoint, body })
// .then((response) => ({
// success: response.success ?? false,
// data: response.comment_token,
// errorMessage: response.BadInput,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
public DeleteComment({
commentToken,
}: {
commentToken: string;
}): Promise<ApiResponse<undefined>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/delete/${commentToken}`;
// public DeleteComment({
// commentToken,
// }: {
// commentToken: string;
// }): Promise<ApiResponse<undefined>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/delete/${commentToken}`;
return this.post<
{ as_mod: boolean },
{
success?: boolean;
BadInput?: string;
}
>({ endpoint, body: { as_mod: true } })
.then((response) => ({
success: response.success ?? false,
errorMessage: response.BadInput,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
// return this.post<
// { as_mod: boolean },
// {
// success?: boolean;
// BadInput?: string;
// }
// >({ endpoint, body: { as_mod: true } })
// .then((response) => ({
// success: response.success ?? false,
// errorMessage: response.BadInput,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
public ListCommentsByEntity({
entityType,
entityToken,
}: {
entityType: string;
entityToken: string;
}): Promise<ApiResponse<string[]>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/list/${entityType}/${entityToken}`;
// public ListCommentsByEntity({
// entityType,
// entityToken,
// }: {
// entityType: string;
// entityToken: string;
// }): Promise<ApiResponse<string[]>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/comments/list/${entityType}/${entityToken}`;
return this.get<{
success: boolean;
comments: string[];
}>({ endpoint })
.then((response) => ({
success: true,
data: response.comments,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
}
// return this.get<{
// success: boolean;
// comments: string[];
// }>({ endpoint })
// .then((response) => ({
// success: true,
// data: response.comments,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
// }
+88 -88
View File
@@ -1,96 +1,96 @@
import { ApiManager, ApiResponse } from "./ApiManager";
// import { ApiManager, ApiResponse } from "../apis/ApiManager";
export class FeaturedItemsApi extends ApiManager {
public CreateFeaturedItem({
entityToken,
entityType,
}: {
entityToken: string;
entityType: string;
}): Promise<ApiResponse<undefined>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/create`;
// export class FeaturedItemsApi extends ApiManager {
// public CreateFeaturedItem({
// entityToken,
// entityType,
// }: {
// entityToken: string;
// entityType: string;
// }): Promise<ApiResponse<undefined>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/create`;
const body = {
entity_token: entityToken,
entity_type: entityType,
};
// const body = {
// entity_token: entityToken,
// entity_type: entityType,
// };
return this.post<
{
entity_token: string;
entity_type: string;
},
{
success?: boolean;
BadInput?: string;
}
>({ endpoint, body })
.then((response) => ({
success: response.success ?? false,
errorMessage: response.BadInput,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
// return this.post<
// {
// entity_token: string;
// entity_type: string;
// },
// {
// success?: boolean;
// BadInput?: string;
// }
// >({ endpoint, body })
// .then((response) => ({
// success: response.success ?? false,
// errorMessage: response.BadInput,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
public DeleteFeaturedItem({
entityToken,
entityType,
}: {
entityToken: string;
entityType: string;
}): Promise<ApiResponse<undefined>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/delete`;
// public DeleteFeaturedItem({
// entityToken,
// entityType,
// }: {
// entityToken: string;
// entityType: string;
// }): Promise<ApiResponse<undefined>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/delete`;
const body = {
entity_token: entityToken,
entity_type: entityType,
};
// const body = {
// entity_token: entityToken,
// entity_type: entityType,
// };
return this.delete<
{
entity_token: string;
entity_type: string;
},
{
success?: boolean;
BadInput?: string;
}
>({ endpoint, body })
.then((response) => ({
success: response.success ?? false,
errorMessage: response.BadInput,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
// return this.delete<
// {
// entity_token: string;
// entity_type: string;
// },
// {
// success?: boolean;
// BadInput?: string;
// }
// >({ endpoint, body })
// .then((response) => ({
// success: response.success ?? false,
// errorMessage: response.BadInput,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
public CheckFeaturedItem({
entityType,
entityToken,
}: {
entityType: string;
entityToken: string;
}): Promise<ApiResponse<boolean>> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/is_featured/${entityType}/${entityToken}`;
// public CheckFeaturedItem({
// entityType,
// entityToken,
// }: {
// entityType: string;
// entityToken: string;
// }): Promise<ApiResponse<boolean>> {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/featured_item/is_featured/${entityType}/${entityToken}`;
return this.post<
undefined,
{
success?: boolean;
is_featured?: boolean;
BadInput?: string;
}
>({ endpoint })
.then((response) => ({
success: response.success ?? false,
data: response.is_featured,
errorMessage: response.BadInput,
}))
.catch((err) => {
return { success: false, errorMessage: err.message };
});
}
}
// return this.post<
// undefined,
// {
// success?: boolean;
// is_featured?: boolean;
// BadInput?: string;
// }
// >({ endpoint })
// .then((response) => ({
// success: response.success ?? false,
// data: response.is_featured,
// errorMessage: response.BadInput,
// }))
// .catch((err) => {
// return { success: false, errorMessage: err.message };
// });
// }
// }
+40 -39
View File
@@ -1,42 +1,43 @@
import { ApiManager, ApiResponse } from "./ApiManager";
// import { ApiManager, ApiResponse } from "../apis/ApiManager";
export class ModerationApi extends ApiManager {
public async UpdateFeatureFlagsByToken({
usernameOrToken,
flags,
}: {
usernameOrToken: string;
flags: string[];
}): Promise<
ApiResponse<{
inference_job_token?: string;
inference_job_token_type?: string;
}>
> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/moderation/user_feature_flags/${usernameOrToken}`;
const body = {
action: {
AddFlags: {
flags,
},
},
};
// export class ModerationApi extends ApiManager {
// public async UpdateFeatureFlagsByToken({
// usernameOrToken,
// flags,
// }: {
// usernameOrToken: string;
// flags: string[];
// }): Promise<
// ApiResponse<{
// inference_job_token?: string;
// inference_job_token_type?: string;
// }>
// > {
// const endpoint = `${this.ApiTargets.BaseApi}/v1/moderation/user_feature_flags/${usernameOrToken}`;
return this.post<
{ action: { AddFlags: { flags: string[] } } },
{ success?: boolean; BadInput?: string }
>({
endpoint,
body: body,
})
.then((response) => ({
success: response.success ?? false,
errorMessage: response.BadInput,
}))
.catch((err) => ({
success: false,
errorMessage: err.message,
}));
}
}
// const body = {
// action: {
// AddFlags: {
// flags,
// },
// },
// };
// return this.post<
// { action: { AddFlags: { flags: string[] } } },
// { success?: boolean; BadInput?: string }
// >({
// endpoint,
// body: body,
// })
// .then((response) => ({
// success: response.success ?? false,
// errorMessage: response.BadInput,
// }))
// .catch((err) => ({
// success: false,
// errorMessage: err.message,
// }));
// }
// }
+2
View File
@@ -36,6 +36,7 @@ export class UsersApi extends ApiManager {
}>
> {
const endpoint = `${this.ApiTargets.BaseApi}/v1/user/${username}/profile`;
console.log("endpoint", endpoint);
return this.get<{
success: boolean;
user?: UserInfo;
@@ -62,6 +63,7 @@ export class UsersApi extends ApiManager {
usernameOrEmail: string;
password: string;
}): Promise<ApiResponse<{ signedSession?: string }>> {
console.log("Logging in with usernameOrEmail:", usernameOrEmail);
const endpoint = `${this.ApiTargets.BaseApi}/v1/login`;
const body = {
username_or_email: usernameOrEmail,
+5
View File
@@ -0,0 +1,5 @@
{
"devDependencies": {
"@tauri-apps/cli": "^2.5.0"
}
}