Merge branch 'main' into github-oauth

This commit is contained in:
neil 2022-12-31 12:46:22 +08:00
commit 23ddf083e9
180 changed files with 1743 additions and 917 deletions

27
.github/notify-slack.js vendored Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env node
async function main() {
const message = {
blocks: [
{
type: 'section',
text: {
type: 'mrkdwn',
text: `NEW BUILD FOR ${process.env.PLATFORM} <${process.env.DOWNLOAD_URL}|download ${process.env.VERSION || ''}>`
}
}
]
}
const res = fetch(process.env.SLACK_WEBHOOK, {
method: 'POST',
body: JSON.stringify(message),
headers: { 'Content-Type': 'application/json' }
})
if (res.ok) {
const data = await res.json();
console.log(data);
}
}
main();

36
.github/update-latest-binary.sh vendored Executable file
View file

@ -0,0 +1,36 @@
#!/bin/bash
git fetch
git checkout main
ARM_CPU=""
IS_M1=$(sysctl -a | grep "Apple M1")
IS_M2=$(sysctl -a | grep "Apple M2")
if [ -z "$IS_M1" ]
then
if [ -z "$IS_M2" ]
then
echo "not valid"
else
ARM_CPU="m2"
fi
else
ARM_CPU="m1"
fi
if ["$ARM_CPU" == ""]
then
echo "nothing to build"
else
tag=$(git tag -l --sort=-creatordate | head -n 1)
pnpm build:gui -b dmg
echo "uploading to s3"
build_path="$PWD/modules/gui/src-tauri/target/release/bundle/dmg/gui_0.1.0_aarch64.dmg"
tag_path="s3://preview.gui.tea.xyz/release/tea_gui_$tag.$ARM_CPU.dmg"
latest_path="s3://preview.gui.tea.xyz/release/tea_gui_latest.$ARM_CPU.dmg"
aws s3 cp $build_path $tag_path
aws s3 cp $tag_path $latest_path
fi

View file

@ -16,16 +16,16 @@ jobs:
with: with:
filters: | filters: |
src: src:
- 'packages/gui/src/**' - 'modules/gui/src/**'
- 'packages/ui/**' - 'modules/ui/**'
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: tauri id: tauri
with: with:
filters: | filters: |
src: src:
- 'packages/gui/src-tauri/**' - 'modules/gui/src-tauri/**'
- 'packages/gui/src/**' - 'modules/gui/src/**'
- 'packages/ui/src/**' - 'modules/ui/src/**'
- name: get s3 preview folder - name: get s3 preview folder
id: preview id: preview
run: echo "folder=${{ github.event.number }}-merge" >> $GITHUB_OUTPUT run: echo "folder=${{ github.event.number }}-merge" >> $GITHUB_OUTPUT
@ -53,7 +53,7 @@ jobs:
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 18
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: install app dependencies - name: install app dependencies
@ -74,7 +74,7 @@ jobs:
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 18
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: install Rust stable - name: install Rust stable
@ -94,7 +94,7 @@ jobs:
env: env:
prefix: ${{ needs.changes.outputs.preview_folder }} prefix: ${{ needs.changes.outputs.preview_folder }}
run: | run: |
aws s3 sync ./packages/gui/build \ aws s3 sync ./modules/gui/build \
"s3://preview.gui.tea.xyz/$prefix" "s3://preview.gui.tea.xyz/$prefix"
- name: Install package - name: Install package
run: sudo apt-get install -y jq coreutils run: sudo apt-get install -y jq coreutils
@ -116,12 +116,19 @@ jobs:
build_tauri: build_tauri:
needs: changes needs: changes
if: needs.changes.outputs.tauri == 'true' if: needs.changes.outputs.tauri == 'true'
runs-on: ${{ matrix.platform.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
platform: [macos-latest, ubuntu-latest] platform:
- os: macos-11
runs-on: ${{ matrix.platform }} name: darwin+x86-64
- os: ubuntu-latest
name: linux+x86-64
- os: [self-hosted, macOS, ARM64]
name: darwin+aarch64
# - os: [self-hosted, linux, ARM64]
# name: linux+aarch64
container: ${{ matrix.platform.container }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -139,15 +146,15 @@ jobs:
with: with:
toolchain: stable toolchain: stable
- name: install dependencies (ubuntu only) - name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest' if: matrix.platform.name == 'linux+x86-64'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf pkg-config
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
with: with:
# The prefix cache key, this can be changed to start a new cache manually. # The prefix cache key, this can be changed to start a new cache manually.
# default: "v0-rust" # default: "v0-rust"
prefix-key: ${{ matrix.platform }} prefix-key: ${{ matrix.platform.name }}
shared-key: ci shared-key: ci
cache-targets: false cache-targets: false
- name: install app dependencies - name: install app dependencies
@ -157,10 +164,23 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
./packages/gui/src-tauri/target ./modules/gui/src-tauri/target
key: ${{ matrix.platform }}-build-target key: ${{ matrix.platform.name }}-build-target
restore-keys: | restore-keys: |
${{ matrix.platform }}-build-target ${{ matrix.platform.name }}-build-target
- name: build platform output
id: build_platform
env:
platform: ${{ matrix.platform.name }}
run: |
EXTENSION=dmg
BUILD_PLATFORM=$(echo $platform | sed -e "s/darwin+//g" | sed -e "s/linux+//g")
[[ $BUILD_PLATFORM = "x86-64" ]] && BUILD_PLATFORM="x64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && BUILD_PLATFORM="amd64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && EXTENSION="deb"
echo "build_platform=$BUILD_PLATFORM" >> $GITHUB_OUTPUT
echo "extension=$EXTENSION" >> $GITHUB_OUTPUT
- name: test build tauri - name: test build tauri
run: pnpm --filter gui tauri build run: pnpm --filter gui tauri build
@ -171,43 +191,22 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1 aws-region: us-east-1
- name: mac-os cp package images from prod to preview bucket - name: cp package images from prod to preview bucket
if: matrix.platform == 'macos-latest'
env: env:
prefix: ${{ needs.changes.outputs.preview_folder }} prefix: ${{ needs.changes.outputs.preview_folder }}
platform: ${{ steps.build_platform.outputs.build_platform }}
extension: ${{ steps.build_platform.outputs.extension }}
run: | run: |
aws s3 cp ./packages/gui/src-tauri/target/release/bundle/dmg/gui_0.1.0_x64.dmg \ aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \
"s3://preview.gui.tea.xyz/$prefix/gui.dmg" "s3://preview.gui.tea.xyz/$prefix/gui_$platform.$extension"
- name: ubuntu cp package images from prod to preview bucket - name: comment install
if: matrix.platform == 'ubuntu-latest'
env:
prefix: ${{ needs.changes.outputs.preview_folder }}
run: |
aws s3 cp ./packages/gui/src-tauri/target/release/bundle/deb/gui_0.1.0_amd64.deb \
"s3://preview.gui.tea.xyz/$prefix/gui.deb"
- name: comment install ubuntu
uses: mshick/add-pr-comment@v2 uses: mshick/add-pr-comment@v2
if: matrix.platform == 'ubuntu-latest'
with: with:
message-id: ${{ matrix.platform }}-comment-${{steps.prefix.outputs.prefix}} message-id: ${{ matrix.platform.name }}-comment
message: | message: |
**installer for ${{ matrix.platform }} is at**: [here](http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{steps.prefix.outputs.prefix}}/gui.deb) **installer for ${{ matrix.platform.name }} is at**: [here](http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{ needs.changes.outputs.preview_folder }}/gui_${{steps.build_platform.outputs.build_platform}}.${{steps.build_platform.outputs.extension}})
```bash ```bash
http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{ needs.changes.outputs.preview_folder }}/gui.deb http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{ needs.changes.outputs.preview_folder }}/gui_${{ steps.build_platform.outputs.build_platform }}.${{ steps.build_platform.outputs.extension }}
```
copy-paste into a browser to download
- name: comment install mac-os
uses: mshick/add-pr-comment@v2
if: matrix.platform == 'macos-latest'
with:
message-id: ${{ matrix.platform }}-comment-${{steps.prefix.outputs.prefix}}
message: |
**installer for ${{ matrix.platform }} is at**: [here](http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{steps.prefix.outputs.prefix}}/gui.dmg)
```bash
http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/${{ needs.changes.outputs.preview_folder }}/gui.dmg
``` ```
copy-paste into a browser to download copy-paste into a browser to download

View file

@ -19,14 +19,16 @@ jobs:
with: with:
filters: | filters: |
src: src:
- 'packages/gui/src/**' - 'modules/gui/src/**'
- 'packages/ui/**' - 'modules/ui/**'
- uses: dorny/paths-filter@v2 - uses: dorny/paths-filter@v2
id: tauri id: tauri
with: with:
filters: | filters: |
src: src:
- 'packages/gui/src-tauri/**' - 'modules/gui/src-tauri/**'
- 'modules/gui/src/**'
- 'modules/ui/src/**'
build_svelte: build_svelte:
needs: changes needs: changes
if: needs.changes.outputs.svelte == 'true' if: needs.changes.outputs.svelte == 'true'
@ -41,7 +43,7 @@ jobs:
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 18
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: install Rust stable - name: install Rust stable
@ -58,12 +60,19 @@ jobs:
build_tauri: build_tauri:
needs: changes needs: changes
if: needs.changes.outputs.tauri == 'true' if: needs.changes.outputs.tauri == 'true'
runs-on: ${{ matrix.platform.os }}
strategy: strategy:
fail-fast: false
matrix: matrix:
platform: [macos-latest, ubuntu-latest] platform:
- os: macos-11
runs-on: ${{ matrix.platform }} name: darwin+x86-64
- os: ubuntu-latest
name: linux+x86-64
- os: [self-hosted, macOS, ARM64]
name: darwin+aarch64
# - os: [self-hosted, linux, ARM64]
# name: linux+aarch64
container: ${{ matrix.platform.container }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -73,7 +82,7 @@ jobs:
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 16 node-version: 18
cache: 'pnpm' cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml cache-dependency-path: pnpm-lock.yaml
- name: install Rust stable - name: install Rust stable
@ -84,11 +93,11 @@ jobs:
with: with:
# The prefix cache key, this can be changed to start a new cache manually. # The prefix cache key, this can be changed to start a new cache manually.
# default: "v0-rust" # default: "v0-rust"
prefix-key: ${{ matrix.platform }} prefix-key: ${{ matrix.platform.name }}
shared-key: prod shared-key: prod
cache-targets: false cache-targets: false
- name: install dependencies (ubuntu only) - name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest' if: matrix.platform.name == 'linux+x86-64'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
@ -99,10 +108,10 @@ jobs:
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: | path: |
./packages/gui/src-tauri/target ./modules/gui/src-tauri/target
key: ${{ matrix.platform }}-build-target-prod key: ${{ matrix.platform.name }}-build-target-prod
restore-keys: | restore-keys: |
${{ matrix.platform }}-build-target-prod ${{ matrix.platform.name }}-build-target-prod
- name: test build tauri - name: test build tauri
run: pnpm --filter gui tauri build run: pnpm --filter gui tauri build
@ -115,14 +124,32 @@ jobs:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1 aws-region: us-east-1
- name: mac-os cp package images from prod to gui bucket
if: matrix.platform == 'macos-latest'
run: |
aws s3 cp ./packages/gui/src-tauri/target/release/bundle/dmg/gui_0.1.0_x64.dmg \
s3://preview.gui.tea.xyz/release/gui_${{ steps.date.outputs.unix_seconds }}.dmg
- name: ubuntu cp package images from prod to gui bucket - name: build platform output
if: matrix.platform == 'ubuntu-latest' id: build_platform
env:
platform: ${{ matrix.platform.name }}
run: | run: |
aws s3 cp ./packages/gui/src-tauri/target/release/bundle/deb/gui_0.1.0_amd64.deb \ BUILD_PLATFORM=$(echo $platform | sed -e "s/darwin+//g" | sed -e "s/linux+//g")
s3://preview.gui.tea.xyz/release/gui_${{ steps.date.outputs.unix_seconds }}.deb EXTENSION=dmg
[[ $BUILD_PLATFORM = "x86-64" ]] && BUILD_PLATFORM="x64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && BUILD_PLATFORM="amd64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && EXTENSION="deb"
echo "build_platform=$BUILD_PLATFORM" >> $GITHUB_OUTPUT
echo "extension=$EXTENSION" >> $GITHUB_OUTPUT
- name: cp package images from prod to gui bucket
env:
platform: ${{ steps.build_platform.outputs.build_platform }}
build_platform: ${{ matrix.platform.name }}
extension: ${{steps.build_platform.outputs.extension}}
run: |
aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \
"s3://preview.gui.tea.xyz/release/tea_${{ steps.date.outputs.unix_seconds }}_$platform.$extension"
- name: Slack Notification
run: ./.github/notify-slack.js
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
PLATFORM: ${{ matrix.platform.name }}
DOWNLOAD_URL: http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/release/tea_${{ steps.date.outputs.unix_seconds }}_${{ steps.build_platform.outputs.build_platform }}.${{ steps.build_platform.outputs.extension }}

107
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,107 @@
on:
push:
tags:
- 'v*.*.*'
jobs:
build_release_tauri:
runs-on: ${{ matrix.platform.os }}
strategy:
matrix:
platform:
- os: macos-11
name: darwin+x86-64
- os: ubuntu-latest
name: linux+x86-64
- os: [self-hosted, macOS, ARM64]
name: darwin+aarch64
# - os: [self-hosted, linux, ARM64]
# name: linux+aarch64
container: ${{ matrix.platform.container }}
steps:
- uses: actions/checkout@v2
- uses: pnpm/action-setup@v2
with:
version: 7
- name: setup node
uses: actions/setup-node@v1
with:
node-version: 18
cache: 'pnpm'
cache-dependency-path: pnpm-lock.yaml
- name: install Rust stable
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: build platform output
id: build_platform
env:
platform: ${{ matrix.platform.name }}
run: |
BUILD_PLATFORM=$(echo $platform | sed -e "s/darwin+//g" | sed -e "s/linux+//g")
EXTENSION=dmg
[[ $BUILD_PLATFORM = "x86-64" ]] && BUILD_PLATFORM="x64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && BUILD_PLATFORM="amd64" || BUILD_PLATFORM=$BUILD_PLATFORM
[[ $platform = "linux+x86-64" ]] && EXTENSION="deb"
echo "build_platform=$BUILD_PLATFORM" >> $GITHUB_OUTPUT
echo "extension=$EXTENSION" >> $GITHUB_OUTPUT
- uses: Swatinem/rust-cache@v2
with:
# The prefix cache key, this can be changed to start a new cache manually.
# default: "v0-rust"
prefix-key: ${{ matrix.platform.name }}
shared-key: prod
cache-targets: false
- name: install dependencies (ubuntu only)
if: matrix.platform.name == 'linux+x86-64'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: install app dependencies
run: pnpm install
- name: Cache Tauri Target
uses: actions/cache@v3
with:
path: |
./modules/gui/src-tauri/target
key: ${{ matrix.platform.name }}-build-target-prod
restore-keys: |
${{ matrix.platform.name }}-build-target-prod
- name: test build tauri
run: pnpm --filter gui tauri build
- name: Get current unix ts - seconds
id: date
run: echo "unix_seconds=$(date +'%s')" >> $GITHUB_OUTPUT
- uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
# update and replace latest release bin in s3
- name: Set tag
id: tag
run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
- name: publish release
env:
platform: ${{ steps.build_platform.outputs.build_platform }}
extension: ${{ steps.build_platform.outputs.extension }}
tag: ${{ steps.tag.outputs.tag }}
run: |
aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \
"s3://preview.gui.tea.xyz/release/tea_gui_latest_$platform.$extension"
aws s3 cp "./modules/gui/src-tauri/target/release/bundle/$extension/tea_0.1.0_$platform.$extension" \
"s3://preview.gui.tea.xyz/release/tea_gui_$tag_$platform.$extension"
- name: Slack Notification
run: ./.github/notify-slack.js
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
PLATFORM: ${{ matrix.platform.name }}
VERSION: ${{steps.tag.outputs.tag}}
DOWNLOAD_URL: http://preview.gui.tea.xyz.s3-website-us-east-1.amazonaws.com/release/tea_gui_${{steps.tag.outputs.tag}}_${{steps.build_platform.outputs.build_platform}}.${{ steps.build_platform.outputs.extension }}

View file

@ -4,18 +4,50 @@ This repository includes the tea GUI/Desktop App.
For better documentation checkout this [notion](https://www.notion.so/teaxyz/tea-gui-fdd9f50aa980432fa370b2cf6a03cb50). For better documentation checkout this [notion](https://www.notion.so/teaxyz/tea-gui-fdd9f50aa980432fa370b2cf6a03cb50).
# Requirements # Requirements
* [pnpm@^7.8](https://pnpm.io/) * [tea - is all you need](https://tea.xyz/)
* [node@16](https://github.com/tj/n)
* [rust@^1.62](https://www.rust-lang.org/)
* [cargo@^1.62](https://crates.io/)
# Development ## Dependencies
Setting up the workspace just run here:
| Project | Version |
|------------|---------|
| nodejs.org | >=16 |
| pnpm.io | >=7.18.2 |
| rust-lang.org | >=1.62 |
# Getting Started
```sh
pnpm install
``` ```
$ pnpm install
To develop the GUI within Tauri Webview
``` ```
$ pnpm dev:gui
```
To develop the GUI within your local browser at localhost:8080
```
$ pnpm web:gui
```
# Build
```sh
pnpm build:gui
```
# Creating a release
Tag any commit in the main branch, then push directly to the main branch.
Lets follow the [semver](https://semver.org/) versioning standard, prefixed with `v`: ie `v1.2.3`
```
$ git tag v1.0.0
$ git push <remote> tag v1.0.0
```
We do not have a runner for building for M1 and M2, to manually deploy a release. Make sure you have a [aws-cli](https://aws.amazon.com/cli/). Configure your aws cli profile correctly.
To publish a release simply run
```
$ AWS_PROFILE=tea/or/etc pnpm release
```
Refer to each package README.md for instructions on how to setup and contribue to them: Refer to each package README.md for instructions on how to setup and contribue to them:
* [tea/gui](./packages/gui/README.md) * [tea/gui](./modules/gui/README.md)
* [tea/ui](./packages/ui/README.md) * [tea/ui](./modules/ui/README.md)

View file

@ -15,10 +15,10 @@
}, },
"devDependencies": { "devDependencies": {
"@playwright/test": "1.25.0", "@playwright/test": "1.25.0",
"@sveltejs/adapter-auto": "next", "@sveltejs/adapter-auto": "^1.0.0",
"@sveltejs/adapter-static": "1.0.0-next.48", "@sveltejs/adapter-static": "^1.0.0",
"@sveltejs/kit": "next", "@sveltejs/kit": "^1.0.1",
"@tauri-apps/cli": "1.2.0", "@tauri-apps/cli": "^1.2.2",
"@tea/ui": "workspace:*", "@tea/ui": "workspace:*",
"@typescript-eslint/eslint-plugin": "^5.27.0", "@typescript-eslint/eslint-plugin": "^5.27.0",
"@typescript-eslint/parser": "^5.27.0", "@typescript-eslint/parser": "^5.27.0",
@ -30,14 +30,14 @@
"prettier": "^2.7.1", "prettier": "^2.7.1",
"prettier-plugin-svelte": "^2.7.0", "prettier-plugin-svelte": "^2.7.0",
"prettier-plugin-tailwindcss": "^0.2.0", "prettier-plugin-tailwindcss": "^0.2.0",
"svelte": "^3.49.0", "svelte": "^3.55.0",
"svelte-check": "^2.8.0", "svelte-check": "^2.8.0",
"svelte-preprocess": "^4.10.7", "svelte-preprocess": "^5.0.0",
"svelte2tsx": "^0.5.20", "svelte2tsx": "^0.5.20",
"tailwindcss": "^3.2.4", "tailwindcss": "^3.2.4",
"tslib": "^2.3.1", "tslib": "^2.3.1",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"vite": "^3.1.0" "vite": "^4.0.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {

View file

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View file

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

View file

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View file

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View file

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

View file

Before

Width:  |  Height:  |  Size: 164 KiB

After

Width:  |  Height:  |  Size: 164 KiB

View file

@ -7,7 +7,7 @@
"distDir": "../build" "distDir": "../build"
}, },
"package": { "package": {
"productName": "gui", "productName": "tea",
"version": "0.1.0" "version": "0.1.0"
}, },
"tauri": { "tauri": {

View file

@ -22,3 +22,20 @@ html {
font-family: sono, sans-serif; font-family: sono, sans-serif;
} }
} }
.text-primary,
header,
h1,
h2,
h3,
h4,
h5,
h6,
button,
.click-copy {
font-family: 'pp-neue-machina' !important;
}
.pk-version {
font-family: 'sono';
}

View file

@ -17,13 +17,7 @@
<ul class="grid grid-cols-3 border border-r-0 border-gray bg-black"> <ul class="grid grid-cols-3 border border-r-0 border-gray bg-black">
{#each category.packages as pkg} {#each category.packages as pkg}
<div class="border border-t-0 border-l-0 border-gray p-4"> <div class="border border-t-0 border-l-0 border-gray p-4">
<MiniPackageCard <MiniPackageCard {pkg} ctaLabel="DETAILS" link={`/packages/${pkg.slug}`} />
{pkg}
ctaLabel="DETAILS"
onClickCTA={async () => {
console.log('do something with:', pkg.full_name);
}}
/>
</div> </div>
{/each} {/each}
</ul> </ul>

View file

@ -1,24 +1,21 @@
<script lang="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import { getAllPosts } from '@api';
import type { AirtablePost } from '@tea/ui/types'; import type { AirtablePost } from '@tea/ui/types';
import Posts from '@tea/ui/Posts/Posts.svelte'; import Posts from '@tea/ui/Posts/Posts.svelte';
import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte'; import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte';
import Preloader from '@tea/ui/Preloader/Preloader.svelte'; import Preloader from '@tea/ui/Preloader/Preloader.svelte';
import { onMount } from 'svelte'; import { postsStore } from '$libs/stores';
let courses: AirtablePost[] = []; let courses: AirtablePost[] = [];
onMount(async () => { postsStore.subscribeByTag('course', (posts) => (courses = posts));
courses = await getAllPosts('course');
});
</script> </script>
<PanelHeader title="Essential Workshops" ctaLabel="View all" ctaLink="/" /> <PanelHeader title="Essential Workshops" ctaLabel="View all" ctaLink="/" />
{#if courses.length} {#if courses.length}
<Posts posts={courses} /> <Posts posts={courses} linkTarget="_blank" />
{:else} {:else}
<section class="h-64 border border-gray p-4"> <section class="h-64 border border-gray bg-black p-4">
<Preloader /> <Preloader />
</section> </section>
{/if} {/if}

View file

@ -1,17 +1,21 @@
<script lang="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import { onMount } from 'svelte'; import { postsStore } from '$libs/stores';
import type { Course } from '$libs/types'; import type { Course } from '$libs/types';
import Gallery from '@tea/ui/Gallery/Gallery.svelte'; import Gallery from '@tea/ui/Gallery/Gallery.svelte';
import { getFeaturedCourses } from '@api';
let courses: Course[] = []; let courses: Course[] = [];
onMount(async () => { postsStore.subscribeByTag('featured_course', (posts) => {
if (!courses.length) { courses = posts.map((post) => {
courses = await getFeaturedCourses(); return {
} title: post.title,
sub_title: post.sub_title,
banner_image_url: post.thumb_image_url,
link: post.link
} as Course;
});
}); });
</script> </script>
@ -23,4 +27,5 @@
imageUrl: course.banner_image_url, imageUrl: course.banner_image_url,
link: course.link link: course.link
}))} }))}
linkTarget="_blank"
/> />

View file

@ -0,0 +1,127 @@
<footer class="h-autofont-machina relative w-full bg-black">
<section class="p-4 px-16 py-16">
<div class="flex">
<div class="footer-text w-3/4 pr-16">
<h3 class="mb-5 text-4xl text-primary">
Changing the way you build. And what happens after you build it.
</h3>
<p class="mb-5">
tea.cli is a delightful package manager that gets out of your way and sets you up for a
joyful development process. You can install it today. Our web3 protocol will create an OSS
ecosystem that is safer for all users, and fair for all maintainers. Read our white paper
here.
</p>
<div class="w-3/4">
<img src="/static/images/footer-grid-element.svg" alt="grid" />
</div>
</div>
<div class="card social-box h-full w-1/4 border-2 border-gray">
<ul class="list-group list-group-flush">
<div class="border-b border-gray p-3">
<a
class="list-group-item"
role="button"
href="https://twitter.com/teaxyz"
target="_blank"
rel="noreferrer"><i class="icon-twitter social-icon mr-3" />Twitter</a
>
</div>
<div class="border-b border-gray p-3">
<a
class="list-group-item"
role="button"
href="https://discord.gg/KCZsXfJphn"
target="_blank"
rel="noreferrer"><i class="icon-discord social-icon mr-3" />Discord</a
>
</div>
<div class="border-b border-gray p-3">
<a
class="list-group-item"
role="button"
href="https://github.com/teaxyz"
target="_blank"
rel="noreferrer"><i class="icon-github social-icon mr-3" />GitHub</a
>
</div>
<div class="border-b border-gray p-3">
<a
class="list-group-item"
role="button"
href="https://reddit.com/r/teaxyz"
target="_blank"
rel="noreferrer"><i class="icon-reddit social-icon mr-3" />Reddit</a
>
</div>
<div class="p-3">
<a
class="list-group-item"
role="button"
href="https://t.me/tea_xyz"
target="_blank"
rel="noreferrer"><i class="icon-telegram social-icon mr-3" />Telegram</a
>
</div>
</ul>
</div>
</div>
</section>
<section class="h-16 border border-r-0 border-gray p-4 px-16">
<div class="flex">
<div class="w-1/2 pt-2">
<p class="text-xs">
©2022 tea inc. You can also share our <a
href="https://linktr.ee/teaxyz"
target="_blank"
rel="noreferrer">Linktree</a
>.
</p>
</div>
<div class="flex w-1/2 pt-2">
<ul class="nav ml-auto flex text-xs">
<li class="nav-item mr-3 ">
<a
class="nav-link footer-link small"
href="https://tea.xyz/privacy-policy/"
target="_blank"
rel="noreferrer">Privacy Policy</a
>
</li>
<li class="nav-item">
<a
class="nav-link footer-link small"
href="https://tea.xyz/terms-of-use/"
target="_blank"
rel="noreferrer">Terms of Use</a
>
</li>
</ul>
</div>
</div>
</section>
</footer>
<style>
h3 {
font-family: 'pp-neue-machina', sans-serif;
color: #00ffd0;
}
p,
.nav-item {
font-family: 'sono', sans-serif;
color: #ffffff;
}
.list-group-item {
font-family: 'pp-neue-machina', sans-serif;
text-transform: uppercase;
transition: 0.1s ease-in;
}
.list-group-item:hover {
padding-left: 1vw;
}
</style>

View file

@ -13,7 +13,7 @@
<ArticleCard <ArticleCard
content={{ content={{
title: 'installing tea', title: 'installing tea',
copy: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, voluptatum molestiae esse quisquam earum debitis.', copy: "It's time to take your first sip! Click below to visit our tea-cli documentation page.",
img_url: '/images/bored-ape.png', img_url: '/images/bored-ape.png',
cta_label: 'Get Started', cta_label: 'Get Started',
link: '/cli' link: '/cli'
@ -24,7 +24,7 @@
<ArticleCard <ArticleCard
content={{ content={{
title: 'authenticating', title: 'authenticating',
copy: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, voluptatum molestiae esse quisquam earum debitis.', copy: 'Using tea without authenticating is like playing a video game without the DLC. Join us today!',
img_url: '/images/bored-ape.png', img_url: '/images/bored-ape.png',
cta_label: 'Get Started', cta_label: 'Get Started',
link: '' link: ''
@ -36,7 +36,7 @@
<ArticleCard <ArticleCard
content={{ content={{
title: 'give us a star', title: 'give us a star',
copy: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, voluptatum molestiae esse quisquam earum debitis.', copy: 'Revolutions are built on the will of the people. Show your support for a more equitable internet.',
img_url: '/images/bored-ape.png', img_url: '/images/bored-ape.png',
cta_label: 'Get Started' cta_label: 'Get Started'
}} }}

View file

@ -3,7 +3,7 @@
import type { GUIPackage } from '$libs/types'; import type { GUIPackage } from '$libs/types';
import { PackageStates } from '$libs/types'; import { PackageStates } from '$libs/types';
import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte'; import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte';
import { packages as packagesStore } from '$libs/stores'; import { packagesStore } from '$libs/stores';
import MiniPackageCard from '@tea/ui/MiniPackageCard/MiniPackageCard.svelte'; import MiniPackageCard from '@tea/ui/MiniPackageCard/MiniPackageCard.svelte';
import Preloader from '@tea/ui/Preloader/Preloader.svelte'; import Preloader from '@tea/ui/Preloader/Preloader.svelte';
let packages: GUIPackage[] = []; let packages: GUIPackage[] = [];

View file

@ -1,16 +1,12 @@
<script type="ts"> <script lang="ts">
import { page } from '$app/stores'; import { page } from '$app/stores';
import { open } from '@tauri-apps/api/shell';
import { appWindow } from '@tauri-apps/api/window'; import { appWindow } from '@tauri-apps/api/window';
import { searchStore } from '$libs/stores';
import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte'; import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte';
import Button from '@tea/ui/Button/Button.svelte'; import Button from '@tea/ui/Button/Button.svelte';
import { beforeUpdate } from 'svelte'; import { beforeUpdate } from 'svelte';
const openGithub = () => {
open('https://github.com/teaxyz');
};
let maximized = false; let maximized = false;
const toggleMaximize = () => { const toggleMaximize = () => {
maximized = !maximized; maximized = !maximized;
@ -41,6 +37,12 @@
path: '/packages', path: '/packages',
active: false, active: false,
label: 'PACKAGES' label: 'PACKAGES'
},
{
path: 'https://github.com/teaxyz',
active: false,
label: 'VIEW ON GITHUB',
target: '_blank'
} }
]; ];
@ -57,13 +59,13 @@
}); });
const onSearch = (term: string) => { const onSearch = (term: string) => {
console.log('navbar search:', term); searchStore.search(term);
}; };
</script> </script>
<ul id="NavBar"> <ul id="NavBar">
<nav data-tauri-drag-region class="flex justify-between"> <nav data-tauri-drag-region class="flex justify-between">
<div class="flex gap-1 p-3"> <div class="flex gap-1 p-3 pt-3">
<button class="titlebar-button" id="titlebar-close" on:click={appWindow.close}> <button class="titlebar-button" id="titlebar-close" on:click={appWindow.close}>
<img src="/images/close.svg" alt="close" /> <img src="/images/close.svg" alt="close" />
</button> </button>
@ -83,14 +85,11 @@
{#each routes as route} {#each routes as route}
<li class={route.active ? 'nav_button active' : 'nav_button'}> <li class={route.active ? 'nav_button active' : 'nav_button'}>
<a href={route.path}> <a href={route.path} target={route.target || ''}>
<Button class="h-16 pl-4 text-left text-white" active={route.active}>{route.label}</Button> <Button class="h-16 pl-4 text-left text-white" active={route.active}>{route.label}</Button>
</a> </a>
</li> </li>
{/each} {/each}
<li class="nav_button">
<Button class="h-16 pl-4 text-left text-white" onClick={openGithub}>VIEW ON GITHUB</Button>
</li>
<footer class="w-full border border-x-0 border-gray"> <footer class="w-full border border-x-0 border-gray">
<a href="/profile"> <a href="/profile">
@ -139,8 +138,8 @@
display: inline-flex; display: inline-flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
width: 16px; width: 12px;
height: 16px; height: 12px;
border-radius: 8px; border-radius: 8px;
opacity: 0.9; opacity: 0.9;
} }

View file

@ -1,24 +1,21 @@
<script lang="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import { getAllPosts } from '@api'; import { postsStore } from '$libs/stores';
import type { AirtablePost } from '@tea/ui/types'; import type { AirtablePost } from '@tea/ui/types';
import Posts from '@tea/ui/Posts/Posts.svelte'; import Posts from '@tea/ui/Posts/Posts.svelte';
import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte'; import PanelHeader from '@tea/ui/PanelHeader/PanelHeader.svelte';
import Preloader from '@tea/ui/Preloader/Preloader.svelte'; import Preloader from '@tea/ui/Preloader/Preloader.svelte';
import { onMount } from 'svelte';
let news: AirtablePost[] = []; let news: AirtablePost[] = [];
onMount(async () => { postsStore.subscribeByTag('news', (posts) => (news = posts));
news = await getAllPosts('news');
});
</script> </script>
<PanelHeader title="Open-source News" ctaLabel="Read more articles >" ctaLink="/" /> <PanelHeader title="Open-source News" ctaLabel="Read more articles >" ctaLink="/" />
{#if news.length} {#if news.length}
<Posts posts={news} /> <Posts posts={news} linkTarget="_blank" />
{:else} {:else}
<section class="h-64 border border-gray p-4"> <section class="h-64 border border-gray bg-black p-4">
<Preloader /> <Preloader />
</section> </section>
{/if} {/if}

View file

@ -34,7 +34,7 @@
</article> </article>
</header> </header>
<footer class="flex h-20 border-t border-gray text-white"> <footer class="flex h-20 border-t border-gray text-white">
<input class="flex-grow bg-black pl-4" disabled value={copyValue} /> <input class="click-copy flex-grow bg-black pl-4" disabled value={copyValue} />
<Button class="w-16 border-0 border-l-2 text-sm" onClick={onCopy}>{copyButtonText}</Button> <Button class="w-16 border-0 border-l-2 text-sm" onClick={onCopy}>{copyButtonText}</Button>
<Button class="w-56 border-0 border-l-2 text-sm" onClick={() => console.log('cli')} <Button class="w-56 border-0 border-l-2 text-sm" onClick={() => console.log('cli')}
>OPEN IN TERMINAL</Button >OPEN IN TERMINAL</Button

View file

@ -6,8 +6,12 @@
import type { Review } from '@tea/ui/types'; import type { Review } from '@tea/ui/types';
export let reviews: Review[]; export let reviews: Review[];
export let showLimit = 9;
let showMore = false;
const getColReviews = (n: number) => { const getColReviews = (n: number) => {
return reviews.filter((_item, i) => (i - n) % 3 === 0); const showReviews = reviews.filter((_item, i) => (i - n) % 3 === 0);
return showMore ? showReviews : showReviews.slice(0, showLimit / 3);
}; };
let col1: Review[] = []; let col1: Review[] = [];
@ -26,22 +30,27 @@
<header class="border border-gray bg-black p-4 text-primary">REVIEWS ({reviews.length})</header> <header class="border border-gray bg-black p-4 text-primary">REVIEWS ({reviews.length})</header>
<section class="flex flex-row flex-wrap bg-black font-machina"> <section class="flex flex-row flex-wrap bg-black font-machina">
<div class="w-1/3 border-0 border-l-2 border-gray p-4"> <div class="w-1/3 border-0 border-l-2 border-b-2 border-gray p-4">
{#each col1 as review} {#each col1 as review}
<ReviewCard {review} /> <ReviewCard {review} />
<div class="mt-4" /> <div class="mt-4" />
{/each} {/each}
</div> </div>
<div class="w-1/3 border-0 border-l-2 border-gray p-4"> <div class="w-1/3 border-0 border-l-2 border-b-2 border-gray p-4">
{#each col2 as review} {#each col2 as review}
<ReviewCard {review} /> <ReviewCard {review} />
<div class="mt-4" /> <div class="mt-4" />
{/each} {/each}
</div> </div>
<div class="w-1/3 border-0 border-x-2 border-gray p-4"> <div class="w-1/3 border-0 border-x-2 border-b-2 border-gray p-4">
{#each col3 as review} {#each col3 as review}
<ReviewCard {review} /> <ReviewCard {review} />
<div class="mt-4" /> <div class="mt-4" />
{/each} {/each}
</div> </div>
</section> </section>
{#if showLimit <= reviews.length && showMore === false}
<footer class="border border-gray bg-black p-4">
<button on:click={() => (showMore = true)}>SHOW MORE</button>
</footer>
{/if}

View file

@ -1,4 +1,4 @@
<script type="ts"> <script lang="ts">
export let label = ''; export let label = '';
</script> </script>

View file

@ -0,0 +1,23 @@
<script lang="ts">
import '$appcss';
</script>
<section class="border-2 border-gray bg-black p-2">
<div class="profile_banner container flex border border-gray bg-black">
<img class="w-1/5" src="/images/bored-ape.png" alt="profile" />
<div class="flex w-4/5 items-center p-5">
<div class="w-1/2 pl-5">
<p class="uppercase text-gray">Authenticated with GitHub</p>
<p />
<p class="text-4xl text-primary">@Username</p>
</div>
<div class="h-full border-l border-gray" />
<div class="w-1/2 pl-10">
<p class="uppercase leading-loose text-gray">
Country: <span>Germany</span><br />Wallet:
<a class="text-green underline" href="/">Connect Now</a>
</p>
</div>
</div>
</div>
</section>

View file

@ -1,21 +1,16 @@
<script type="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import Fuse from 'fuse.js'; import { packagesStore } from '$libs/stores';
import { packages as packagesStore, initializePackages } from '$libs/stores';
import SortingButtons from './SortingButtons.svelte'; import SortingButtons from './SortingButtons.svelte';
import type { GUIPackage } from '$libs/types'; import type { GUIPackage } from '$libs/types';
import { PackageStates } from '$libs/types'; import { PackageStates } from '$libs/types';
import PackageCard from '@tea/ui/PackageCard/PackageCard.svelte'; import PackageCard from '@tea/ui/PackageCard/PackageCard.svelte';
import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte'; import SearchInput from '@tea/ui/SearchInput/SearchInput.svelte';
import Preloader from '@tea/ui/Preloader/Preloader.svelte'; import Preloader from '@tea/ui/Preloader/Preloader.svelte';
import { onMount } from 'svelte';
import { installPackage } from '@api'; import { installPackage } from '@api';
let allPackages: GUIPackage[] = [];
let packagesIndex: Fuse<GUIPackage>;
let packages: GUIPackage[] = []; let packages: GUIPackage[] = [];
let initialized = false;
let sortBy = 'popularity'; let sortBy = 'popularity';
let sortDirection: 'asc' | 'desc' = 'desc'; let sortDirection: 'asc' | 'desc' = 'desc';
@ -39,32 +34,12 @@
}); });
}; };
packagesStore.subscribe((v) => { const onSearch = async (term: string) => {
allPackages = v;
setPackages(allPackages);
if (!packagesIndex && allPackages.length) {
// dont remove or this can get crazy
packagesIndex = new Fuse(allPackages, {
keys: ['name', 'full_name', 'desc']
});
}
});
onMount(async () => {
if (!packages.length && !initialized) {
initialized = true;
initializePackages();
}
});
const onSearch = (term: string) => {
if (term !== '' && term.length > 1) { if (term !== '' && term.length > 1) {
const res = packagesIndex.search(term, { limit: searchLimit }); const matchingPackages: GUIPackage[] = await packagesStore.search(term, searchLimit);
const matchingPackages: GUIPackage[] = res.map((v) => v.item);
setPackages(matchingPackages, true); setPackages(matchingPackages, true);
} else { } else {
setPackages(allPackages); setPackages(packagesStore.packages, false);
} }
}; };
@ -82,6 +57,8 @@
[PackageStates.UNINSTALLED]: 'RE-INSTALL' [PackageStates.UNINSTALLED]: 'RE-INSTALL'
}[state]; }[state];
}; };
packagesStore.subscribe(setPackages);
</script> </script>
<div class="border border-gray bg-black"> <div class="border border-gray bg-black">

View file

@ -0,0 +1,160 @@
<script lang="ts">
import { searchStore } from '$libs/stores';
import type { GUIPackage } from '$libs/types';
import Preloader from '@tea/ui/Preloader/Preloader.svelte';
import PackageCard from '@tea/ui/PackageCard/PackageCard.svelte';
import { PackageStates } from '$libs/types';
import Posts from '@tea/ui/Posts/Posts.svelte';
import { installPackage } from '@api';
import type { AirtablePost } from '@tea/ui/types';
let term: string;
let packages: GUIPackage[] = [];
let articles: AirtablePost[] = []; // news, blogs, etc
let workshops: AirtablePost[] = []; // workshops, course
let loading = true;
searchStore.subscribe((v) => {
term = v;
});
searchStore.packagesSearch.subscribe((pkgs) => {
packages = pkgs;
});
searchStore.postsSearch.subscribe((posts) => {
let partialArticles: AirtablePost[] = [];
let partialWorkshops: AirtablePost[] = [];
for (let post of posts) {
if (post.tags.includes('news')) {
partialArticles.push(post);
}
if (post.tags.includes('course') || post.tags.includes('featured_course')) {
partialWorkshops.push(post);
}
}
articles = partialArticles;
workshops = partialWorkshops;
});
searchStore.searching.subscribe((v) => (loading = v));
const getCTALabel = (state: PackageStates): string => {
return {
[PackageStates.AVAILABLE]: 'INSTALL',
[PackageStates.INSTALLED]: 'INSTALLED',
[PackageStates.INSTALLING]: 'INSTALLING',
[PackageStates.UNINSTALLED]: 'RE-INSTALL'
}[state];
};
const onClose = () => {
term = '';
};
</script>
<section class={term ? 'show' : ''}>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<figure on:click={onClose} />
<div class="z-20 border border-gray bg-black">
<header class="flex justify-between p-4">
<div class="text-2xl text-primary">Showing search results for `{term}`</div>
<button on:click={onClose}>&#x2715</button>
</header>
<menu class="flex h-8 w-full gap-4 bg-accent px-4 text-xs">
<button>packages ({packages.length})</button>
<button>articles ({articles.length})</button>
<button>workshops ({workshops.length})</button>
</menu>
<header class="p-4 text-lg text-primary">
Top Package Results ({packages.length})
</header>
<ul class="grid grid-cols-3">
{#if packages.length > 0}
{#each packages as pkg}
<div class={pkg.state === PackageStates.INSTALLING ? 'animate-pulse' : ''}>
<PackageCard
{pkg}
link={`/packages/${pkg.slug}`}
ctaLabel={getCTALabel(pkg.state)}
onClickCTA={async () => {
try {
pkg.state = PackageStates.INSTALLING;
await installPackage(pkg.full_name);
pkg.state = PackageStates.INSTALLED;
} catch (error) {
console.error(error);
}
}}
/>
</div>
{/each}
{:else if loading}
{#each Array(12) as _}
<section class="h-50 border border-gray p-4">
<Preloader />
</section>
{/each}
{/if}
</ul>
<header class="p-4 text-lg text-primary">
Top Article Results ({articles.length})
</header>
{#if articles.length}
<Posts posts={articles} linkTarget="_blank" />
{:else if loading}
<section class="h-64 border border-gray bg-black p-4">
<Preloader />
</section>
{/if}
<header class="p-4 text-lg text-primary">
Top Workshop Results ({workshops.length})
</header>
{#if workshops.length}
<Posts posts={workshops} linkTarget="_blank" />
{:else if loading}
<section class="h-64 border border-gray bg-black p-4">
<Preloader />
</section>
{/if}
</div>
</section>
<style>
section {
position: fixed;
top: 0;
left: 240px;
right: 0;
background: rgba(0, 0, 0, 0.7);
transition: opacity 0.3s ease-in-out;
padding: 36px;
opacity: 0%;
overflow: hidden;
height: 0px;
z-index: 10;
}
section > figure {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
section.show {
opacity: 100%;
height: 100%;
}
section > div {
position: relative;
height: 0%;
transition: height 0.6s ease-in-out;
overflow-y: scroll;
}
section.show > div {
height: 90%;
}
</style>

View file

@ -172,7 +172,7 @@ export async function getFeaturedPackages(): Promise<Package[]> {
export async function getPackageReviews(full_name: string): Promise<Review[]> { export async function getPackageReviews(full_name: string): Promise<Review[]> {
console.log(`generating reviews for ${full_name}`); console.log(`generating reviews for ${full_name}`);
const reviewCount = _.random(7, 21); const reviewCount = _.random(9, 21);
const reviews: Review[] = []; const reviews: Review[] = [];
for (let i = 0; i < reviewCount; i++) { for (let i = 0; i < reviewCount; i++) {

View file

@ -29,7 +29,8 @@ async function get<T>(path: string, query?: { [key: string]: string }) {
console.log('uri:', uri); console.log('uri:', uri);
const { data } = await client.get<T>(uri.toString(), { const { data } = await client.get<T>(uri.toString(), {
headers: { headers: {
Authorization: 'public' // TODO: figure out why req w/o Authorization does not work Authorization: 'public', // TODO: figure out why req w/o Authorization does not work
'cache-control': 'no-cache'
}, },
query: query || {} query: query || {}
}); });
@ -73,8 +74,9 @@ export async function getFeaturedPackages(): Promise<Package[]> {
export async function getPackageReviews(full_name: string): Promise<Review[]> { export async function getPackageReviews(full_name: string): Promise<Review[]> {
console.log(`getting reviews for ${full_name}`); console.log(`getting reviews for ${full_name}`);
const reviews: Review[] = await mock.getPackageReviews(full_name); const reviews: Review[] = await get<Review[]>(
`packages/${full_name.replaceAll('/', ':')}/reviews`
);
return reviews; return reviews;
} }
@ -154,9 +156,9 @@ export async function getTopPackages(): Promise<GUIPackage[]> {
return packages; return packages;
} }
export async function getAllPosts(tag: string): Promise<AirtablePost[]> { export async function getAllPosts(tag?: string): Promise<AirtablePost[]> {
// add filter here someday: tag = news | course // add filter here someday: tag = news | course
const posts = await get<AirtablePost[]>('posts', { tag }); const posts = await get<AirtablePost[]>('posts', tag ? { tag } : {});
return posts; return posts;
} }

View file

@ -0,0 +1,172 @@
import { writable } from 'svelte/store';
import Fuse from 'fuse.js';
import type { Package, Review, AirtablePost } from '@tea/ui/types';
import type { GUIPackage } from '$libs/types';
// TODO: figure out a better structure for managing states maybe turn them into separate files?
import { getPackages, getFeaturedPackages, getPackageReviews, getAllPosts } from '@api';
export const backLink = writable<string>('/');
export const featuredPackages = writable<Package[]>([]);
function initPackagesStore() {
let initialized = false;
const { subscribe, set } = writable<GUIPackage[]>([]);
const packages: GUIPackage[] = [];
let packagesIndex: Fuse<GUIPackage>;
if (!initialized) {
initialized = true;
getPackages().then((pkgs) => {
set(pkgs);
packagesIndex = new Fuse(pkgs, {
keys: ['name', 'full_name', 'desc']
});
});
}
subscribe((v) => packages.push(...v));
return {
packages,
subscribe,
search: async (term: string, limit = 5): Promise<GUIPackage[]> => {
if (!term || !packagesIndex) return [];
// TODO: if online, use algolia else use Fuse
const res = packagesIndex.search(term, { limit });
const matchingPackages: GUIPackage[] = res.map((v) => v.item);
return matchingPackages;
}
};
}
export const packagesStore = initPackagesStore();
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 { 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();
function initPosts() {
let initialized = false;
const { subscribe, set } = writable<AirtablePost[]>([]);
const posts: AirtablePost[] = [];
let postsIndex: Fuse<AirtablePost>;
if (!initialized) {
initialized = true;
getAllPosts().then(set);
}
subscribe((v) => {
posts.push(...v);
postsIndex = new Fuse(posts, {
keys: ['title', 'sub_title', 'short_description', 'tags']
});
});
return {
subscribe,
search: async (term: string, limit = 10) => {
const res = postsIndex.search(term, { limit });
const matchingPosts: AirtablePost[] = res.map((v) => v.item);
return matchingPosts;
},
subscribeByTag: (tag: string, cb: (posts: AirtablePost[]) => void) => {
subscribe((newPosts: AirtablePost[]) => {
const filteredPosts = newPosts.filter((post) => post.tags.includes(tag));
cb(filteredPosts);
});
}
};
}
export const postsStore = initPosts();
function initSearchStore() {
const searching = writable<boolean>(false);
const { subscribe, set } = writable<string>('');
const packagesSearch = writable<GUIPackage[]>([]);
const postsSearch = writable<AirtablePost[]>([]);
// TODO:
// should use algolia if user is somehow online
const packagesFound: GUIPackage[] = [];
let term = '';
subscribe((v) => (term = v));
packagesSearch.subscribe((v) => packagesFound.push(...v));
return {
term,
searching,
packagesSearch,
postsSearch,
packagesFound,
subscribe,
search: async (term: string) => {
searching.set(true);
try {
if (term) {
const [resultPkgs, resultPosts] = await Promise.all([
packagesStore.search(term, 5),
postsStore.search(term, 10)
]);
packagesSearch.set(resultPkgs);
postsSearch.set(resultPosts);
} else {
packagesSearch.set([]);
postsSearch.set([]);
}
set(term);
} finally {
searching.set(false);
}
}
};
}
export const searchStore = initSearchStore();

View file

@ -1,35 +1,43 @@
<!-- home / discover / welcome page --> <!-- home / discover / welcome page -->
<script lang="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import { navigating } from '$app/stores';
import NavBar from '$components/NavBar/NavBar.svelte'; import NavBar from '$components/NavBar/NavBar.svelte';
import FooterLinks from '$components/FooterLinks/FooterLinks.svelte'; import FooterLinks from '$components/FooterLinks/FooterLinks.svelte';
import { backLink as backLinkStore } from '$libs/stores'; import { backLink as backLinkStore } from '$libs/stores';
import SearchPopupResults from '$components/SearchPopupResults/SearchPopupResults.svelte';
let view: HTMLElement;
let backLink = ''; let backLink = '';
backLinkStore.subscribe((v) => { backLinkStore.subscribe((v) => {
backLink = v; backLink = v;
}); });
$: if ($navigating) view.scrollTop = 0;
</script> </script>
<div id="main-layout"> <div id="main-layout">
<nav class="border border-t-0 border-l-0 border-b-0 border-gray"> <nav class="border border-t-0 border-l-0 border-b-0 border-gray">
<NavBar /> <NavBar />
</nav> </nav>
<section class="pt-24"> <section class="pt-24" bind:this={view}>
{#if backLink} {#if backLink}
<header class="px-16 py-2 text-3xl text-gray hover:text-primary"> <header class="border-b border-gray px-16 text-3xl text-gray hover:text-primary">
<a href={backLink}>&#8592</a> <a href={backLink}>&#8592</a>
</header> </header>
{/if} {/if}
<figure /> <figure />
<div class="px-16">
<div class="content">
<!-- all pages get inserted in this slot --> <!-- all pages get inserted in this slot -->
<slot /> <slot />
</div> </div>
<footer class="mt-8 w-full border border-r-0 border-gray bg-black"> <footer class="mt-8 w-full border border-r-0 border-gray bg-black">
<FooterLinks /> <FooterLinks />
</footer> </footer>
<SearchPopupResults />
</section> </section>
</div> </div>
@ -49,22 +57,40 @@
height: 100vh; height: 100vh;
overflow-y: scroll; overflow-y: scroll;
} }
figure { figure {
position: fixed;
z-index: 0; z-index: 0;
position: fixed;
top: 220px; top: 220px;
left: 240px; left: 240px;
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
background-image: url('/images/footer-grid-element.svg'); background-image: url('/images/gui-background-grid.svg');
}
@media screen and (min-width: 1440px) {
figure {
background-size: cover;
background-repeat: repeat-y;
}
.content {
padding: 0vw 3.6vw !important;
}
}
@media screen and (max-width: 1440px) {
figure {
background-size: contain;
background-repeat: repeat;
}
.content {
padding: 0vw 3.33vw;
}
} }
header { header {
position: absolute; position: absolute;
top: 0px; top: 0px;
left: 0px; left: 0px;
width: 100%; width: 100%;
height: 50px; height: 40px;
border-bottom: #ccc 1px solid;
} }
slot { slot {

View file

@ -1,36 +1,37 @@
<script type="ts"> <script lang="ts">
import '$appcss'; import '$appcss';
import PageHeader from '$components/PageHeader/PageHeader.svelte'; import PageHeader from '$components/PageHeader/PageHeader.svelte';
import { backLink } from '$libs/stores'; import { backLink, packagesReviewStore } from '$libs/stores';
import PackageBanner from '$components/PackageBanner/PackageBanner.svelte'; import PackageBanner from '$components/PackageBanner/PackageBanner.svelte';
// import PackageReviews from '$components/PackageReviews/PackageReviews.svelte'; import PackageReviews from '$components/PackageReviews/PackageReviews.svelte';
import type { Review } from '@tea/ui/types';
backLink.set('/packages'); backLink.set('/packages');
/** @type {import('./$types').PageData} */ /** @type {import('./$types').PageData} */
export let data; export let data;
import { packages, featuredPackages } from '$libs/stores'; import { packagesStore, featuredPackages } from '$libs/stores';
import type { Package } from '@tea/ui/types'; import type { Package } from '@tea/ui/types';
let pkg: Package; let pkg: Package;
// let reviews: Review[]; let reviews: Review[];
const setPkg = (pkgs: Package[]) => { const setPkg = (pkgs: Package[]) => {
const foundPackage = pkgs.find(({ slug }) => slug === data?.slug) as Package; const foundPackage = pkgs.find(({ slug }) => slug === data?.slug) as Package;
if (!pkg && foundPackage) { if (!pkg && foundPackage) {
pkg = foundPackage; pkg = foundPackage;
} }
// TODO: uncomment when api is ready
// if (!reviews && pkg) { if (!reviews && pkg) {
// packagesReviewStore.subscribe(pkg.full_name, (updatedReviews) => { packagesReviewStore.subscribe(pkg.full_name, (updatedReviews) => {
// reviews = updatedReviews; reviews = updatedReviews;
// }); });
// } }
}; };
packages.subscribe(setPkg); packagesStore.subscribe(setPkg);
featuredPackages.subscribe(setPkg); featuredPackages.subscribe(setPkg);
</script> </script>
@ -39,10 +40,7 @@
<section> <section>
<PackageBanner {pkg} /> <PackageBanner {pkg} />
</section> </section>
<!--
TODO: uncomment when api is ready
<section class="mt-8"> <section class="mt-8">
<PackageReviews reviews={reviews || []} /> <PackageReviews reviews={reviews || []} />
</section> </section>
-->
</div> </div>

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 8.3 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View file

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

Before

Width:  |  Height:  |  Size: 764 KiB

After

Width:  |  Height:  |  Size: 764 KiB

View file

Before

Width:  |  Height:  |  Size: 232 B

After

Width:  |  Height:  |  Size: 232 B

View file

Before

Width:  |  Height:  |  Size: 176 B

After

Width:  |  Height:  |  Size: 176 B

View file

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 26 KiB

View file

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 137 B

View file

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Some files were not shown because too many files have changed in this diff Show more