Skip to content

Commit

Permalink
Add (parts of) privacy policy
Browse files Browse the repository at this point in the history
  • Loading branch information
RikudouSage committed Jun 3, 2024
1 parent 38023d8 commit 84f5bc0
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 4 deletions.
26 changes: 25 additions & 1 deletion src/app/pages/privacy/privacy.component.html
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
<p>privacy works!</p>
@if (policyItems()) {
<section class="bg-gray-50 dark:bg-gray-800">
<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">

@for (sectionPair of policyItems()!|keyvalue:NoSorterKeyValue; track sectionPair) {
<h2 class="mb-6 mt-12 text-4xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{sectionPair.key}}</h2>

@for (subsectionPair of sectionPair.value|keyvalue:NoSorterKeyValue; track subsectionPair) {
@if (subsectionPair.key) {
<h3 class="mb-6 mt-6 text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{subsectionPair.key}}</h3>
}
@for (item of subsectionPair.value; track item.text) {
<div class="mb-3 tracking-tight underline-links" [innerHTML]="
item.text | replace:'{li}':'&nbsp;&nbsp;&#9679;&nbsp;'
"></div>
}
}
}
</div>
</div>
</div>
</section>
}
5 changes: 5 additions & 0 deletions src/app/pages/privacy/privacy.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
h2, h3 {
&:first-of-type {
margin-top: 0;
}
}
32 changes: 29 additions & 3 deletions src/app/pages/privacy/privacy.component.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,38 @@
import { Component } from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {DataService} from "../../services/data.service";
import {toPromise} from "../../types/resolvable";
import {toSignal} from "@angular/core/rxjs-interop";
import {InlineSvgComponent} from "../../components/inline-svg/inline-svg.component";
import {TranslocoMarkupComponent} from "ngx-transloco-markup";
import {FooterColorService} from "../../services/footer-color.service";
import {KeyValuePipe} from "@angular/common";
import {NoSorterKeyValue} from "../../types/no-sorter-key-value";
import {ReplacePipe} from "../../pipes/replace.pipe";

@Component({
selector: 'app-privacy',
standalone: true,
imports: [],
imports: [
InlineSvgComponent,
TranslocoMarkupComponent,
KeyValuePipe,
ReplacePipe
],
templateUrl: './privacy.component.html',
styleUrl: './privacy.component.scss'
})
export class PrivacyComponent {
export class PrivacyComponent implements OnInit {
public policyItems = toSignal(this.dataService.privacyPolicy);

constructor(
private readonly dataService: DataService,
private readonly footerColor: FooterColorService,
) {
}

public async ngOnInit(): Promise<void> {
this.footerColor.dark.set(true);
}

protected readonly NoSorterKeyValue = NoSorterKeyValue;
}
13 changes: 13 additions & 0 deletions src/app/pipes/replace.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({
name: 'replace',
standalone: true
})
export class ReplacePipe implements PipeTransform {

transform(value: string, replaceWhat: string, replaceWith: string): string {
return value.replaceAll(replaceWhat, replaceWith);
}

}
41 changes: 41 additions & 0 deletions src/app/services/data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {FaqItem} from "../types/faq-item";
import {TranslocoService} from "@jsverse/transloco";
import {ToolItem} from "../types/tool-item";
import {SortedItems} from "../types/sorted-items";
import {PrivacyPolicyItem} from "../types/privacy-policy-item";

type ObjectWithMappableKey<TObject, TKey extends keyof TObject> = TObject[TKey] extends string | null ? TObject : never;

Expand All @@ -30,6 +31,46 @@ export class DataService {
);
}

public get privacyPolicy(): Observable<Map<string, Map<string, PrivacyPolicyItem[]>>> {
return this.getData<PrivacyPolicyItem>('privacy').pipe(
map (items => {
const sorted = this.formatToMapped(items, 'section');
const result: Map<string, Map<string, PrivacyPolicyItem[]>> = new Map<string, Map<string, PrivacyPolicyItem[]>>();

sorted.forEach((value, key) => {
const subSorted = this.formatToMapped(
value.map(item => {
const copy = {...item};
if (copy.context !== undefined) {
for (const key of Object.keys(copy.context)) {
const contextItem = copy.context[key];
let targetContextValue: string;
switch (contextItem.valueType) {
case "date":
targetContextValue = new Intl.DateTimeFormat(this.transloco.getActiveLang(), {
dateStyle: 'long',
timeStyle: undefined,
}).format(new Date(contextItem.value));
break;
default:
throw new Error(`Unsupported type: ${contextItem.valueType}`);
}

copy.text = copy.text.replaceAll(`{${key}}`, targetContextValue);
}
}
return copy;
}),
'subsection',
);
result.set(key, subSorted);
});

return result;
}),
);
}

private formatToMapped<TObject, TKey extends keyof TObject>(objects: ObjectWithMappableKey<TObject, TKey>[], key: TKey): SortedItems<TObject> {
const result: SortedItems<TObject> = new Map<string, TObject[]>();
for (const object of objects) {
Expand Down
11 changes: 11 additions & 0 deletions src/app/types/privacy-policy-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface PrivacyPolicyItem {
text: string;
section: string;
subsection: string | null;
context?: {
[key: string]: {
valueType: 'date';
value: string;
};
};
}
148 changes: 148 additions & 0 deletions src/assets/data/privacy.en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
[
{
"text": "Last updated: {lastUpdatedDate}",
"section": "Privacy Policy",
"subsection": null,
"context": {
"lastUpdatedDate": {
"valueType": "date",
"value": "2024-03-18"
}
}
},
{
"text": "This Privacy Policy describes Our policies and procedures on the collection, use and disclosure of Your information when You use the Service and tells You about Your privacy rights and how the law protects You.",
"section": "Privacy Policy",
"subsection": null
},
{
"text": "We use Your Personal data to provide and improve the Service. By using the Service, You agree to the collection and use of information in accordance with this Privacy Policy. This Privacy Policy has been created with the help of the <a href='https://www.privacypolicies.com/privacy-policy-generator/' target='_blank'>Privacy Policy Generator</a>.",
"section": "Privacy Policy",
"subsection": null
},
{
"text": "The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.",
"section": "Interpretation and Definitions",
"subsection": "Interpretation"
},
{
"text": "For the purposes of this Privacy Policy:",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Account</strong> means a unique account created for You to access our Service or parts of our Service.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Company</strong> (referred to as either \"the Company\", \"We\", \"Us\" or \"Our\" in this Agreement) refers to AI Horde.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Cookies</strong> are small files that are placed on Your computer, mobile device or any other device by a website, containing the details of Your browsing history on that website among its many uses.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Country</strong> refers to: Luxembourg",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Device</strong> means any device that can access the Service such as a computer, a cellphone or a digital tablet.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Personal Data</strong> is any information that relates to an identified or identifiable individual.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Service</strong> refers to the Website.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Service Provider</strong> means any natural or legal person who processes the data on behalf of the Company. It refers to third-party companies or individuals employed by the Company to facilitate the Service, to provide the Service on behalf of the Company, to perform services related to the Service or to assist the Company in analyzing how the Service is used.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Usage Data</strong> refers to data collected automatically, either generated by the use of the Service or from the Service infrastructure itself (for example, the duration of a page visit).",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Website</strong> refers to AI Horde, accessible from <a href='/'>https://aihorde.net</a>",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>You</strong> means the individual accessing or using the Service, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Worker</strong> means the individual behind a node generating images or text, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Requestor</strong> means the individual providing the prompt reruesting the generation of images or text, or the company, or other legal entity on behalf of which such individual is accessing or using the Service, as applicable.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "{li} <strong>Prompt</strong> refers to the text string sent to generate an image using generative models.",
"section": "Interpretation and Definitions",
"subsection": "Definitions"
},
{
"text": "",
"section": "Collecting and Using Your Personal Data",
"subsection": null
},
{
"text": "Our Service does not require any personal data to use it. You are welcome to use it completely anonymously.",
"section": "Types of Data Collected",
"subsection": "Personal Data"
},
{
"text": "While using Our Service, you may opt-in to provide Us with certain personally identifiable information that can be used to contact or identify You. Personally identifiable information may include, but is not limited to:",
"section": "Types of Data Collected",
"subsection": "Personal Data"
},
{
"text": "{li} Email address",
"section": "Types of Data Collected",
"subsection": "Personal Data"
},
{
"text": "Usage Data is collected automatically when using the Service.",
"section": "Types of Data Collected",
"subsection": "Usage Data"
},
{
"text": "Usage Data may include information such as Your Device's Internet Protocol address (e.g. IP address) and the prompts you used to generate images.",
"section": "Types of Data Collected",
"subsection": "Usage Data"
},
{
"text": "On an opt-in basis you may provide us with a contact email to use in case of problems with your Workers but this is not used in any other way.",
"section": "Use of Your Personal Data",
"subsection": null
},
{
"text": "When registering with an oauth2 provider, we maintain a record of the ID number for your provider without any other identifying information. We only use this to connect your oauth provider to your AI Horde account.",
"section": "Use of Your Personal Data",
"subsection": null
},
{
"text": "The described data storage is necessary for the provision and protection of our online services. The legal basis is Recital 49 of the General Data Protection Regulation (GDPR).",
"section": "Use of Your Personal Data",
"subsection": null
}
]

0 comments on commit 84f5bc0

Please sign in to comment.