mirror of
https://github.com/ivabus/gui
synced 2025-06-08 00:00:27 +03:00
commit
713c10db74
11 changed files with 246 additions and 42 deletions
|
@ -43,6 +43,8 @@
|
|||
"@tauri-apps/api": "^1.2.0",
|
||||
"buffer": "^6.0.3",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lorem-ipsum": "^2.0.8",
|
||||
"svelte-watch-resize": "^1.0.3"
|
||||
},
|
||||
"pnpm": {
|
||||
|
|
|
@ -1,6 +1,47 @@
|
|||
<script lang="ts">
|
||||
import '$appcss';
|
||||
import Placeholder from '$components/Placeholder/Placeholder.svelte';
|
||||
import { afterUpdate } from 'svelte';
|
||||
import ReviewCard from '@tea/ui/ReviewCard/ReviewCard.svelte';
|
||||
|
||||
import type { Review } from '@tea/ui/types';
|
||||
export let reviews: Review[];
|
||||
|
||||
const getColReviews = (n: number) => {
|
||||
return reviews.filter((_item, i) => (i - n) % 3 === 0);
|
||||
};
|
||||
|
||||
let col1: Review[] = [];
|
||||
let col2: Review[] = [];
|
||||
let col3: Review[] = [];
|
||||
|
||||
afterUpdate(() => {
|
||||
col1 = getColReviews(0);
|
||||
col2 = getColReviews(1);
|
||||
col3 = getColReviews(2);
|
||||
});
|
||||
|
||||
// TODO: problem with reviews with differing heights
|
||||
// ideally they should work like metro-ui to not have extreme height diff between columns
|
||||
</script>
|
||||
|
||||
<Placeholder label="PackageReviews" />
|
||||
<header class="text-primary bg-black border border-gray p-4">REVIEWS ({reviews.length})</header>
|
||||
<section class="flex flex-wrap bg-black flex-row font-machina">
|
||||
<div class="border-gray border-0 border-l-2 p-4 w-1/3">
|
||||
{#each col1 as review}
|
||||
<ReviewCard {review} />
|
||||
<div class="mt-4" />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="border-gray border-0 border-l-2 p-4 w-1/3">
|
||||
{#each col2 as review}
|
||||
<ReviewCard {review} />
|
||||
<div class="mt-4" />
|
||||
{/each}
|
||||
</div>
|
||||
<div class="border-gray border-0 p-4 border-x-2 w-1/3">
|
||||
{#each col3 as review}
|
||||
<ReviewCard {review} />
|
||||
<div class="mt-4" />
|
||||
{/each}
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
const searchLimit = 5;
|
||||
|
||||
const setPackages = (pkgs: Package[]) => {
|
||||
console.log('pkgs sub', pkgs);
|
||||
packages = pkgs.sort((a, b) => {
|
||||
if (sortBy === 'popularity') {
|
||||
const aPop = +a.dl_count + a.installs;
|
||||
|
@ -36,9 +37,12 @@
|
|||
packagesStore.subscribe((v) => {
|
||||
allPackages = v;
|
||||
setPackages(allPackages);
|
||||
if (!packagesIndex) {
|
||||
// dont remove or this can get crazy
|
||||
packagesIndex = new Fuse(allPackages, {
|
||||
keys: ['name', 'full_name', 'desc']
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
* TODO:
|
||||
* * make cors work with api.tea.xyz/v1
|
||||
*/
|
||||
import type { Package } from '../types';
|
||||
import type { Package, Review } from '@tea/ui/types';
|
||||
import { loremIpsum } from 'lorem-ipsum';
|
||||
import _ from 'lodash';
|
||||
|
||||
const packages: Package[] = [
|
||||
{
|
||||
slug: 'mesonbuild_com',
|
||||
|
@ -156,3 +159,41 @@ export async function getPackages(): Promise<Package[]> {
|
|||
export async function getFeaturedPackages(): Promise<Package[]> {
|
||||
return packages.slice(0, 4);
|
||||
}
|
||||
|
||||
export async function getPackageReviews(full_name: string): Promise<Review[]> {
|
||||
console.log(`generating reviews for ${full_name}`);
|
||||
|
||||
const reviewCount = _.random(7, 21);
|
||||
const reviews: Review[] = [];
|
||||
|
||||
for (let i = 0; i < reviewCount; i++) {
|
||||
const title = loremIpsum({
|
||||
count: _.random(2, 5),
|
||||
format: 'plain',
|
||||
paragraphLowerBound: 3,
|
||||
paragraphUpperBound: 7,
|
||||
random: Math.random,
|
||||
sentenceLowerBound: 5,
|
||||
sentenceUpperBound: 15,
|
||||
units: 'words'
|
||||
});
|
||||
const comment = loremIpsum({
|
||||
count: 2,
|
||||
format: 'plain',
|
||||
paragraphLowerBound: 3,
|
||||
paragraphUpperBound: 7,
|
||||
random: Math.random,
|
||||
sentenceLowerBound: 5,
|
||||
sentenceUpperBound: 15,
|
||||
units: 'sentences'
|
||||
});
|
||||
const rating = _.random(0, 5);
|
||||
reviews.push({
|
||||
title,
|
||||
comment,
|
||||
rating
|
||||
});
|
||||
}
|
||||
|
||||
return reviews;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
*/
|
||||
import { getClient } from '@tauri-apps/api/http';
|
||||
import { Buffer } from 'buffer';
|
||||
import type { Package } from '../types';
|
||||
import type { Package, Review } from '../types';
|
||||
import * as mock from './mock';
|
||||
|
||||
const username = 'user';
|
||||
|
@ -55,3 +55,10 @@ export async function getFeaturedPackages(): Promise<Package[]> {
|
|||
const packages = await mock.getFeaturedPackages();
|
||||
return packages;
|
||||
}
|
||||
|
||||
export async function getPackageReviews(full_name: string): Promise<Review[]> {
|
||||
console.log(`getting reviews for ${full_name}`);
|
||||
const reviews: Review[] = await mock.getPackageReviews(full_name);
|
||||
|
||||
return reviews;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { writable } from 'svelte/store';
|
||||
import type { Package } from './types';
|
||||
import type { Package, Review } from '@tea/ui/types';
|
||||
|
||||
import { getPackages, getFeaturedPackages } from '@api';
|
||||
// TODO: figure out a better structure for managing states maybe turn them into models?
|
||||
|
||||
import { getPackages, getFeaturedPackages, getPackageReviews } from '@api';
|
||||
|
||||
export const backLink = writable<string>('/');
|
||||
|
||||
|
@ -10,11 +12,51 @@ export const packages = writable<Package[]>([]);
|
|||
export const featuredPackages = writable<Package[]>([]);
|
||||
|
||||
export const initializePackages = async () => {
|
||||
console.log('initialize packages');
|
||||
const newPackages = await getPackages();
|
||||
packages.set(newPackages);
|
||||
};
|
||||
|
||||
export const initializeFeaturedPackages = async () => {
|
||||
console.log('initialzie featured packages');
|
||||
const packages = await getFeaturedPackages();
|
||||
featuredPackages.set(packages);
|
||||
};
|
||||
|
||||
interface PackagesReview {
|
||||
[full_name: string]: Review[];
|
||||
}
|
||||
|
||||
function initPackagesReviewStore() {
|
||||
const { set, update, subscribe } = writable<PackagesReview>({});
|
||||
|
||||
let packagesReviews: PackagesReview = {};
|
||||
|
||||
subscribe((v) => (packagesReviews = v));
|
||||
|
||||
const getSetPackageReviews = async (full_name: string) => {
|
||||
if (full_name && !packagesReviews[full_name]) {
|
||||
console.log('getting reviews for', full_name);
|
||||
const reviews = await getPackageReviews(full_name);
|
||||
update((v) => {
|
||||
return {
|
||||
...v,
|
||||
[full_name]: reviews
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
subscribe: (full_name: string, reset: (reviews: Review[]) => void) => {
|
||||
getSetPackageReviews(full_name);
|
||||
return subscribe((value) => {
|
||||
if (value[full_name]) {
|
||||
reset(value[full_name]);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const packagesReviewStore = initPackagesReviewStore();
|
||||
|
|
|
@ -9,17 +9,24 @@
|
|||
/** @type {import('./$types').PageData} */
|
||||
export let data;
|
||||
|
||||
import { packages, featuredPackages } from '$libs/stores';
|
||||
import { packages, featuredPackages, packagesReviewStore } from '$libs/stores';
|
||||
|
||||
import type { Package } from '@tea/ui/types';
|
||||
import type { Package, Review } from '@tea/ui/types';
|
||||
|
||||
let pkg: Package;
|
||||
|
||||
let reviews: Review[];
|
||||
|
||||
const setPkg = (pkgs: Package[]) => {
|
||||
const foundPackage = pkgs.find(({ slug }) => slug === data?.slug) as Package;
|
||||
if (!pkg && foundPackage) {
|
||||
pkg = foundPackage;
|
||||
}
|
||||
if (!reviews && pkg) {
|
||||
packagesReviewStore.subscribe(pkg.full_name, (updatedReviews) => {
|
||||
reviews = updatedReviews;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
packages.subscribe(setPkg);
|
||||
|
@ -32,6 +39,6 @@
|
|||
<PackageBanner {pkg} />
|
||||
</section>
|
||||
<section class="mt-8">
|
||||
<PackageReviews />
|
||||
<PackageReviews reviews={reviews || []} />
|
||||
</section>
|
||||
</div>
|
||||
|
|
30
packages/ui/src/ReviewCard/ReviewCard.stories.ts
Normal file
30
packages/ui/src/ReviewCard/ReviewCard.stories.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import ReviewCard from './ReviewCard.svelte';
|
||||
|
||||
// More on how to set up stories at: https://storybook.js.org/docs/7.0/svelte/writing-stories/introduction
|
||||
export default {
|
||||
title: 'Example/ReviewCard',
|
||||
component: ReviewCard,
|
||||
tags: ['docsPage'],
|
||||
render: ({ review }) => ({
|
||||
Component: ReviewCard,
|
||||
props: { review }
|
||||
}),
|
||||
argTypes: {
|
||||
review: {
|
||||
name: 'review',
|
||||
description: 'this is type Review'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// More on writing stories with args: https://storybook.js.org/docs/7.0/svelte/writing-stories/args
|
||||
export const Example = {
|
||||
args: {
|
||||
review: {
|
||||
title: 'installing tea',
|
||||
comment:
|
||||
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, voluptatum molestiae esse quisquam earum debitis.',
|
||||
rating: 2
|
||||
}
|
||||
}
|
||||
};
|
35
packages/ui/src/ReviewCard/ReviewCard.svelte
Normal file
35
packages/ui/src/ReviewCard/ReviewCard.svelte
Normal file
|
@ -0,0 +1,35 @@
|
|||
<script type="ts">
|
||||
import '../app.css';
|
||||
import type { Review } from '../types';
|
||||
|
||||
export let review: Review;
|
||||
|
||||
const getStarType = () => {
|
||||
let star = 'full';
|
||||
if (review.rating < 3) {
|
||||
star = 'empty';
|
||||
} else if (review.rating < 4) {
|
||||
star = 'half';
|
||||
}
|
||||
return `icon-star-${star}`;
|
||||
};
|
||||
|
||||
const getStarLabel = () => {
|
||||
let label = 'DELIGHTFUL';
|
||||
if (review.rating < 3) {
|
||||
label = 'NOT DELIGHTFUL';
|
||||
} else if (review.rating < 4) {
|
||||
label = 'DELIGHTFUL WITH NOTES';
|
||||
}
|
||||
return label;
|
||||
};
|
||||
</script>
|
||||
|
||||
<section class="border border-gray p-4">
|
||||
<header class=" text-primary text-lg">{review.title}</header>
|
||||
<div class="flex mt-2 text-xs">
|
||||
<i class={`${getStarType()} text-primary`} />
|
||||
<span class="text-gray pl-2">{getStarLabel()}</span>
|
||||
</div>
|
||||
<p class="mt-2 text-sm">{review.comment}</p>
|
||||
</section>
|
|
@ -1,3 +1,9 @@
|
|||
export interface Review {
|
||||
title: string;
|
||||
comment: string;
|
||||
rating: number;
|
||||
created_at?: Date | string;
|
||||
}
|
||||
export interface Package {
|
||||
slug: string;
|
||||
version: string;
|
||||
|
@ -11,4 +17,5 @@ export interface Package {
|
|||
desc: string;
|
||||
dl_count: number;
|
||||
installs: number;
|
||||
reviews?: Review[];
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ importers:
|
|||
eslint-config-prettier: ^8.3.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
fuse.js: ^6.6.2
|
||||
lodash: ^4.17.21
|
||||
lorem-ipsum: ^2.0.8
|
||||
postcss: ^8.4.19
|
||||
prettier: ^2.6.2
|
||||
prettier-plugin-svelte: ^2.7.0
|
||||
|
@ -38,12 +40,14 @@ importers:
|
|||
'@tauri-apps/api': 1.2.0
|
||||
buffer: 6.0.3
|
||||
fuse.js: 6.6.2
|
||||
lodash: 4.17.21
|
||||
lorem-ipsum: 2.0.8
|
||||
svelte-watch-resize: 1.0.3
|
||||
devDependencies:
|
||||
'@playwright/test': 1.25.0
|
||||
'@sveltejs/adapter-auto': 1.0.0-next.90
|
||||
'@sveltejs/adapter-static': 1.0.0-next.48
|
||||
'@sveltejs/kit': 1.0.0-next.567_svelte@3.53.1+vite@3.2.4
|
||||
'@sveltejs/kit': 1.0.0-next.570_svelte@3.53.1+vite@3.2.4
|
||||
'@tauri-apps/cli': 1.2.0
|
||||
'@tea/ui': link:../ui
|
||||
'@typescript-eslint/eslint-plugin': 5.43.0_wze2rj5tow7zwqpgbdx2buoy3m
|
||||
|
@ -3206,34 +3210,6 @@ packages:
|
|||
resolution: {integrity: sha512-Z5Z+QZOav6D0KDeU3ReksGERJg/sX1k5OKWWXyQ11OwGErEEwSXHYRUyjaBmZEPeGzpVVGwwMUK8YWJlG/MKeA==}
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.567_svelte@3.53.1+vite@3.2.4:
|
||||
resolution: {integrity: sha512-4Q74HPLZb9Rr60n7EJANXH2Ng3d7wwrz67TuXxVfYuxARigWO9YzjcLHxG2IVMwRZIeT+LlwOKkg9vYYdBiezQ==}
|
||||
engines: {node: '>=16.14'}
|
||||
hasBin: true
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
svelte: ^3.44.0
|
||||
vite: ^3.2.0
|
||||
dependencies:
|
||||
'@sveltejs/vite-plugin-svelte': 1.3.1_svelte@3.53.1+vite@3.2.4
|
||||
'@types/cookie': 0.5.1
|
||||
cookie: 0.5.0
|
||||
devalue: 4.2.0
|
||||
kleur: 4.1.5
|
||||
magic-string: 0.26.7
|
||||
mime: 3.0.0
|
||||
sade: 1.8.1
|
||||
set-cookie-parser: 2.5.1
|
||||
sirv: 2.0.2
|
||||
svelte: 3.53.1
|
||||
tiny-glob: 0.2.9
|
||||
undici: 5.13.0
|
||||
vite: 3.2.4
|
||||
transitivePeerDependencies:
|
||||
- diff-match-patch
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@sveltejs/kit/1.0.0-next.570_svelte@3.53.1+vite@3.2.4:
|
||||
resolution: {integrity: sha512-7CUoYidoWlKdTGxL/5RsPPkgGTb36TwFBnhSZmspHFeiIST5qQEXyzqicFk8+4M5qQhxvOZ0NuP8uy7Pc+xf/Q==}
|
||||
engines: {node: '>=16.14'}
|
||||
|
@ -4634,6 +4610,11 @@ packages:
|
|||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/commander/9.4.1:
|
||||
resolution: {integrity: sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==}
|
||||
engines: {node: ^12.20.0 || >=14}
|
||||
dev: false
|
||||
|
||||
/commondir/1.0.1:
|
||||
resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
|
||||
dev: true
|
||||
|
@ -7341,7 +7322,6 @@ packages:
|
|||
|
||||
/lodash/4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
|
||||
/longest-streak/3.1.0:
|
||||
resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
|
||||
|
@ -7354,6 +7334,14 @@ packages:
|
|||
js-tokens: 4.0.0
|
||||
dev: true
|
||||
|
||||
/lorem-ipsum/2.0.8:
|
||||
resolution: {integrity: sha512-5RIwHuCb979RASgCJH0VKERn9cQo/+NcAi2BMe9ddj+gp7hujl6BI+qdOG4nVsLDpwWEJwTVYXNKP6BGgbcoGA==}
|
||||
engines: {node: '>= 8.x', npm: '>= 5.x'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
commander: 9.4.1
|
||||
dev: false
|
||||
|
||||
/lower-case/2.0.2:
|
||||
resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
|
||||
dependencies:
|
||||
|
|
Loading…
Reference in a new issue