Skip to content

Commit

Permalink
Feat: Donate to educator account (RikudouSage#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikudouSage authored and tazlin committed Jul 20, 2024
1 parent 764dbb7 commit 382c425
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<label class="toggle" for="input{{random()}}">
<label class="toggle dark:text-white text-gray-900" for="input{{random()}}">
<input class="toggle-checkbox" type="checkbox" id="input{{random()}}" [checked]="value()" (change)="value.set(input.checked)" #input>
<div class="toggle-switch"></div>
<span class="toggle-label">{{description()}}</span>
Expand Down
27 changes: 22 additions & 5 deletions src/app/pages/transfer/transfer.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ <h1 class="mb-4 text-4xl font-extrabold leading-none tracking-tight md:text-5xl
<div class="max-w-screen-xl px-4 py-8 mx-auto space-y-12 lg:space-y-20 lg:py-24 lg:px-6">
<div class="items-center gap-8 lg:grid xl:gap-16">
<div class="text-gray-500 sm:text-lg dark:text-gray-400">
<p class="mb-4"><transloco key="transfer.visit_old" [params]="{oldUrl: '/transfer'}" /></p>
<p class="mb-6"><transloco key="transfer.visit_old" [params]="{oldUrl: '/transfer'}" /></p>
<form class="max-w-sm mx-auto" [formGroup]="form" (ngSubmit)="transfer()">
<div class="mb-2">
<label for="source_api_key" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{'transfer.source_api_key' | transloco}}</label>
<label for="source_api_key" class="block mb-2 font-medium text-gray-900 dark:text-white">{{'transfer.source_api_key' | transloco}}</label>
<input formControlName="apiKey" type="text" id="source_api_key" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required [placeholder]="'transfer.source_api_key.description' | transloco" />
</div>
@if (form.value.apiKeyValidated === false) {
Expand All @@ -29,16 +29,28 @@ <h1 class="mb-4 text-4xl font-extrabold leading-none tracking-tight md:text-5xl
<app-toggle-checkbox [description]="'transfer.source_api_key.remember' | transloco" formControlName="remember" />
</div>

<div [class.mb-5]="form.value.targetUserValidated !== false" [class.mb-1]="form.value.targetUserValidated === false">
<label for="target_user" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{'transfer.target_user' | transloco}}</label>
<div [class.mb-1]="educatorAccounts()" [class.mb-5]="!educatorAccounts()">
<label for="target_user" class="block mb-2 font-medium text-gray-900 dark:text-white">{{'transfer.target_user' | transloco}}</label>
<input formControlName="targetUser" type="text" id="target_user" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required [placeholder]="'transfer.target_user.description' | transloco:{exampleUser: exampleUser()}" />
</div>
@if (educatorAccounts()) {
<div class="dark:text-white text-gray-900" [class.mb-5]="form.value.targetUserValidated !== false" [class.mb-1]="form.value.targetUserValidated === false">
<p class="mb-1"><small>{{'transfer.donate' | transloco}}<sup><a routerLink="/transfer-v2" fragment="note1" class="text-blue-600 dark:text-green-400">1</a></sup></small></p>

<select formControlName="educatorAccount" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
@for (user of educatorAccounts(); track user.id) {
<option [value]="null">{{'option.empty' | transloco}}</option>
<option [value]="user.id">{{user.username}} ({{'transfer.account.kudos_amount' | transloco:{amountFormatted:user.kudos | formatNumber} }})</option>
}
</select>
</div>
}
@if (form.value.targetUserValidated === false) {
<p class="mb-5 text-red"><small>{{'invalid_target_user' | transloco}}</small></p>
}

<div [class.mb-5]="form.value.kudosAmountValidated !== false" [class.mb-1]="form.value.kudosAmountValidated === false">
<label for="kudos_amount" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">{{'transfer.kudos_amount' | transloco}}</label>
<label for="kudos_amount" class="block mb-2 font-medium text-gray-900 dark:text-white">{{'transfer.kudos_amount' | transloco}}</label>
<input formControlName="kudosAmount" type="number" min="1" id="kudos_amount" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500" required [placeholder]="'transfer.kudos_amount.description' | transloco:{user: form.value.targetUser || ('transfer.kudos_amount.description.user_placeholder' | transloco)}" />
</div>
@if (form.value.kudosAmountValidated === false) {
Expand All @@ -62,6 +74,11 @@ <h1 class="mb-4 text-4xl font-extrabold leading-none tracking-tight md:text-5xl
[class.text-white]="form.valid"
type="submit" class="hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm w-full sm:w-auto px-5 py-2.5 text-center dark:hover:bg-blue-700 dark:focus:ring-blue-800">Submit</button>
</form>

<p id="note1" class="mt-6" [class.active]="fragment() === 'note1'">
<sup>1</sup>
{{'educators.explanation' | transloco}}
</p>
</div>
</div>
</div>
Expand Down
43 changes: 40 additions & 3 deletions src/app/pages/transfer/transfer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import {debounceTime} from "rxjs";
import {Subscriptions} from "../../helper/subscriptions";
import {AiHordeService} from "../../services/ai-horde.service";
import {HordeUser} from "../../types/horde-user";
import {toSignal} from "@angular/core/rxjs-interop";
import {FormatNumberPipe} from "../../pipes/format-number.pipe";
import {ActivatedRoute, RouterLink} from "@angular/router";

@Component({
selector: 'app-transfer',
Expand All @@ -25,7 +28,9 @@ import {HordeUser} from "../../types/horde-user";
TranslocoPipe,
ToggleCheckboxComponent,
ReactiveFormsModule,
JsonPipe
JsonPipe,
FormatNumberPipe,
RouterLink
],
templateUrl: './transfer.component.html',
styleUrl: './transfer.component.scss'
Expand All @@ -50,14 +55,18 @@ export class TransferComponent implements OnInit, OnDestroy {
}

return this.currentUser()!.kudos;
})
});
public educatorAccounts = toSignal(this.aiHorde.getEducatorAccounts());
public fragment = signal<string|null>(null);

public form = new FormGroup({
apiKey: new FormControl<string>('', [Validators.required]),
remember: new FormControl<boolean>(false),
targetUser: new FormControl<string>('', [Validators.required]),
kudosAmount: new FormControl<number>(1, [Validators.required, Validators.min(1)]),

educatorAccount: new FormControl<number | null>(null),

apiKeyValidated: new FormControl<boolean | null>(null, [Validators.requiredTrue]),
targetUserValidated: new FormControl<boolean | null>(null, [Validators.requiredTrue]),
kudosAmountValidated: new FormControl<boolean | null>(null, [Validators.requiredTrue]),
Expand All @@ -69,6 +78,7 @@ export class TransferComponent implements OnInit, OnDestroy {
private readonly footerColor: FooterColorService,
private readonly database: DatabaseService,
private readonly aiHorde: AiHordeService,
public readonly activatedRoute: ActivatedRoute,
) {
}

Expand All @@ -89,6 +99,26 @@ export class TransferComponent implements OnInit, OnDestroy {
kudosAmount ??= 0;
this.form.patchValue({kudosAmountValidated: this.maximumKudos() !== null && kudosAmount <= this.maximumKudos()!});
}));
this.subscriptions.add(this.form.controls.educatorAccount.valueChanges.subscribe(accountId => {
// @ts-ignore
if (accountId === 'null') {
accountId = null;
}

if (!accountId) {
this.form.controls.targetUser.enable();
this.form.patchValue({targetUser: ''});
return;
}

// @ts-ignore
accountId = Number(accountId);
const account = this.educatorAccounts()!.filter(account => account.id === accountId)[0];

this.form.controls.targetUser.disable();
this.form.patchValue({targetUser: account.username});
}));

this.subscriptions.add(this.form.controls.apiKey.valueChanges.pipe(
debounceTime(500)
).subscribe(async apiKey => {
Expand Down Expand Up @@ -134,6 +164,13 @@ export class TransferComponent implements OnInit, OnDestroy {

this.sentSuccessfully.set(null);
}));

this.subscriptions.add(this.activatedRoute.fragment.subscribe(fragment => {
this.fragment.set(fragment);
if (fragment) {
document.querySelector(`#${fragment}`)?.scrollIntoView();
}
}));
}

public ngOnDestroy(): void {
Expand All @@ -148,7 +185,7 @@ export class TransferComponent implements OnInit, OnDestroy {

const success = await toPromise(this.aiHorde.transferKudos(
this.form.value.apiKey!,
this.form.value.targetUser!,
this.form.controls.targetUser.value!,
this.form.value.kudosAmount!,
));
this.sentSuccessfully.set(success);
Expand Down
13 changes: 12 additions & 1 deletion src/app/services/ai-horde.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Injectable} from '@angular/core';
import {HttpClient} from "@angular/common/http";
import {catchError, map, Observable, of} from "rxjs";
import {catchError, map, Observable, of, zip} from "rxjs";
import {ImageTotalStats} from "../types/image-total-stats";
import {HordePerformance} from "../types/horde-performance";
import {TextTotalStats} from "../types/text-total-stats";
Expand Down Expand Up @@ -107,4 +107,15 @@ export class AiHordeService {
catchError(() => of(false)),
);
}

public getEducatorAccounts(): Observable<HordeUser[]> {
// todo once filtering is available, filter it on the api, this is only temporary solution
const userIds = [258170];

return zip(
userIds.map(userId => this.getUserById(userId).pipe(
map(user => user!),
))
)
}
}
8 changes: 6 additions & 2 deletions src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
"transfer": "Transfer kudos",
"transfer.visit_old": "If you would like to log in using one of the account providers (Google, Discord, Github), you might need to use [link:oldUrl]the old version of the transfer page[/link].",
"transfer.source_api_key": "Your API key",
"transfer.target_user": "Unique user ID",
"transfer.target_user": "The receiving user",
"transfer.target_user.description": "For example: {{exampleUser}}",
"transfer.source_api_key.description": "Type the API key that belongs to your user",
"transfer.source_api_key.remember": "Remember the API key?",
Expand All @@ -87,5 +87,9 @@
"transfer.kudos_amount.description.user_placeholder": "the user",
"transfer.success": "The kudos have been successfully transferred!",
"transfer.error": "There was an error while transferring the kudos, please try again later.",
"transfer.too_many_kudos": "Eh, you don't have that many kudos"
"transfer.too_many_kudos": "Eh, you don't have that many kudos",
"transfer.donate": "or donate them to one of the educator accounts:",
"option.empty": "-- none --",
"transfer.account.kudos_amount": "has {{amountFormatted}} kudos",
"educators.explanation": "An educator account provides teachers and educational institutions with access to AI Horde for generating AI images."
}

0 comments on commit 382c425

Please sign in to comment.