diff --git a/.forgejo/workflows/deploy-dev.yml b/.forgejo/workflows/deploy-dev.yml index 26053ff..19d5a78 100644 --- a/.forgejo/workflows/deploy-dev.yml +++ b/.forgejo/workflows/deploy-dev.yml @@ -10,18 +10,98 @@ jobs: steps: - name: Check out repo uses: https://data.forgejo.org/actions/checkout@v4 + with: + fetch-depth: 0 - - name: Setup Bun + - name: Install Packages + run: apt-get update && apt-get install -y sudo curl jq + + - name: Install Nix + uses: https://github.com/cachix/install-nix-action@v30 + + - name: Install Bun uses: oven-sh/setup-bun@v2 - name: Install dependencies run: bun install --frozen-lockfile + - name: Setup Android Keystore + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PROPERTIES: ${{ secrets.ANDROID_KEYSTORE_PROPERTIES }} + run: | + bun -e "require('fs').writeFileSync('keystore.jks', Buffer.from(process.env.KEYSTORE_BASE64.replace(/\s+/g, ''), 'base64'))" + bun -e "const content = process.env.KEYSTORE_PROPERTIES.replace(/\\\\n/g, '\n').replace(/\\r/g, '').split('\n').map(l => l.trim()).filter(l => l).join('\n'); require('fs').writeFileSync('keystore.properties', content)" + - name: Build - run: bun run build:web + run: bun run build:apps - name: Install rsync run: apt-get update && apt-get install -y rsync - name: Deploy run: rsync -a --delete apps/web/dist/ /var/lib/www/tensamin-web-dev/ + + - name: Read version and hash + id: version + run: | + VERSION="$(node -p "require('./package.json').version")" + SHORT_SHA="$(git rev-parse --short HEAD)" + echo "version=$VERSION" >> "$FORGEJO_OUTPUT" + echo "short_sha=$SHORT_SHA" >> "$FORGEJO_OUTPUT" + echo "tag=dev-${VERSION}-${SHORT_SHA}" >> "$FORGEJO_OUTPUT" + echo "title=${VERSION}-dev-${SHORT_SHA}" >> "$FORGEJO_OUTPUT" + + - name: Create pre-release and upload files + env: + TOKEN: ${{ forgejo.token }} + API: ${{ forgejo.api_url }} + REPO: ${{ forgejo.repository }} + SHA: ${{ forgejo.sha }} + TAG: ${{ steps.version.outputs.tag }} + TITLE: ${{ steps.version.outputs.title }} + run: | + set -eu + + test -d releases + find releases -type f | grep -q . + + LATEST_PROD_TAG="$(git tag --list 'v*' --sort=-v:refname | head -n 1 || true)" + if [ -n "$LATEST_PROD_TAG" ]; then + BODY="$(git log "$LATEST_PROD_TAG"..HEAD --pretty=format:'- %s')" + else + BODY="$(git log --pretty=format:'- %s')" + fi + + HTTP_STATUS=$(curl -s -w "%{http_code}" -o release_out.json -H "Authorization: token $TOKEN" "$API/repos/$REPO/releases/tags/$TAG") + + if [ "$HTTP_STATUS" = "200" ]; then + echo "Release $TAG already exists." + RELEASE_ID="$(jq -r .id release_out.json)" + else + echo "Creating new pre-release for $TAG" + RELEASE_JSON="$(curl -f -sS -X POST "$API/repos/$REPO/releases" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d "$(jq -n \ + --arg tag "$TAG" \ + --arg name "$TITLE" \ + --arg body "$BODY" \ + --arg target "$SHA" \ + '{ + tag_name: $tag, + name: $name, + body: $body, + target_commitish: $target, + draft: false, + prerelease: true + }')")" + RELEASE_ID="$(echo "$RELEASE_JSON" | jq -r .id)" + fi + + find releases -type f -print0 | while IFS= read -r -d '' file; do + name="$(basename "$file")" + curl -fsS -X POST "$API/repos/$REPO/releases/$RELEASE_ID/assets?name=$name" \ + -H "Authorization: token $TOKEN" \ + -F "attachment=@$file" + done diff --git a/.forgejo/workflows/deploy-prod.yml b/.forgejo/workflows/deploy-prod.yml index 1df5b60..c2f5c62 100644 --- a/.forgejo/workflows/deploy-prod.yml +++ b/.forgejo/workflows/deploy-prod.yml @@ -54,13 +54,14 @@ jobs: REPO: ${{ forgejo.repository }} SHA: ${{ forgejo.sha }} TAG: ${{ steps.version.outputs.tag }} - VERSION: ${{ steps.version.outputs.version }} run: | set -eu test -d releases find releases -type f | grep -q . + COMMIT_MSG="$(git log -1 --pretty=%B)" + HTTP_STATUS=$(curl -s -w "%{http_code}" -o release_out.json -H "Authorization: token $TOKEN" "$API/repos/$REPO/releases/tags/$TAG") if [ "$HTTP_STATUS" = "200" ]; then @@ -74,7 +75,7 @@ jobs: -d "$(jq -n \ --arg tag "$TAG" \ --arg name "$TAG" \ - --arg body "Release $VERSION" \ + --arg body "$COMMIT_MSG" \ --arg target "$SHA" \ '{ tag_name: $tag, diff --git a/apps/web/src/components/sidebar.tsx b/apps/web/src/components/sidebar.tsx index ce9bb6b..6b4b6cd 100644 --- a/apps/web/src/components/sidebar.tsx +++ b/apps/web/src/components/sidebar.tsx @@ -6,6 +6,7 @@ import { Sidebar as SidebarRoot, SidebarContent, SidebarFooter, + useSidebar, } from "@tensamin/ui"; import { isTauri } from "@tauri-apps/api/core"; import { useIsMobile } from "@tensamin/ui"; @@ -13,17 +14,21 @@ import { MobileNavbar } from "./navbar"; import SidebarBox from "@tensamin/call/sidebarBox"; import { useShowMobileNavbar } from "@/routes/app/layout"; - /** * Renders the conversation sidebar with account summary and conversation list. + * On mobile the sidebar is always kept in the DOM and hidden via CSS + * (opacity + translateX) instead of being unmounted. This keeps the DOM and + * React state alive while the drawer is closed, allowing the sidebar to open + * instantly on subsequent toggles. * @returns Sidebar JSX. */ export default function Sidebar() { const isMobile = useIsMobile(); const showMobileNavbar = useShowMobileNavbar(); + const { openMobile, setOpenMobile } = useSidebar(); - return ( - + const content = ( + <> )} - + ); + + if (isMobile) { + return ( + <> + {openMobile && ( +
setOpenMobile(false)} + /> + )} +
+
{content}
+
+ + ); + } + + return {content}; }