diff --git a/.github/actions/find-or-create-comment/action.yml b/.github/actions/find-or-create-comment/action.yml
new file mode 100644
index 000000000..5d435abb0
--- /dev/null
+++ b/.github/actions/find-or-create-comment/action.yml
@@ -0,0 +1,41 @@
+# Creates a comment to show native preview build status
+# Or finds the comment if it exists
+# Exposes the comment id in env in either case
+name: Find or create comment
+description: 'Finds the comment of build status or create one. Outputs the comment id.'
+
+inputs:
+ github-token:
+ description: 'Github token'
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Find or create comment
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ inputs.github-token }}
+ script: |
+ const buildName = '${{ env.build-name }}';
+ const commentMagicPrefix = '${{ env.comment-unique-magic-prefix }}';
+ const comments = await github.rest.issues.listComments({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ const existingComment = comments.data.find(comment => comment.body.startsWith(commentMagicPrefix));
+
+ if (existingComment) {
+ core.exportVariable('comment_id', existingComment.id)
+ } else {
+ const commentBody = `${commentMagicPrefix}\nš ${buildName} build has started... Please wait for the results! š`;
+ const { data: { id: commentId } } = await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: commentBody
+ });
+ core.exportVariable('comment_id', commentId)
+ }
diff --git a/.github/actions/update-native-preview-build-status/action.yml b/.github/actions/update-native-preview-build-status/action.yml
new file mode 100644
index 000000000..66c42cdbd
--- /dev/null
+++ b/.github/actions/update-native-preview-build-status/action.yml
@@ -0,0 +1,39 @@
+name: Update build status
+description: 'Updates build status comment with the build results'
+
+inputs:
+ github-token:
+ description: 'Github token'
+ required: true
+ build-outcome:
+ description: 'Build outcome'
+ required: true
+
+runs:
+ using: 'composite'
+ steps:
+ - name: Update build status
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ inputs.github-token }}
+ script: |
+ const commentId = '${{ env.comment_id }}';
+ const buildOutcome = '${{ inputs.build-outcome }}';
+ const buildStatus = buildOutcome == 'success' ? 'completed' : 'failed';
+ const buildName = '${{ env.build-name }}';
+ const workflowUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
+
+ let commentBody = `${{ env.comment-unique-magic-prefix }}\n${buildName} build ${buildStatus}!`;
+
+ if (buildOutcome == 'success') {
+ commentBody += `\nYou can download the ${buildName} from the following link:\n${workflowUrl}#artifacts`;
+ } else {
+ commentBody += '\nPlease check the workflow logs for more details on the build failure.';
+ }
+
+ await github.rest.issues.updateComment({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ comment_id: commentId,
+ body: commentBody
+ });
\ No newline at end of file
diff --git a/.github/workflows/android-build-manual.yml b/.github/workflows/android-build-manual.yml
deleted file mode 100644
index 3aa325482..000000000
--- a/.github/workflows/android-build-manual.yml
+++ /dev/null
@@ -1,53 +0,0 @@
-name: android build manual
-
-on:
- workflow_dispatch:
- inputs:
- name:
- description: 'Build manually'
- default: 'World'
- required: true
- type: string
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- update:
- name: EAS Android Preview Build
- runs-on: ubuntu-latest
- permissions:
- contents: read
- pull-requests: write
- steps:
- - name: Check for EXPO_TOKEN
- run: |
- if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
- echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets. Learn more: https://docs.expo.dev/eas-update/github-actions"
- exit 1
- fi
-
- - name: Checkout repository
- uses: actions/checkout@v3
-
- - name: Setup Node
- uses: actions/setup-node@v3
- with:
- node-version: 20.x
- cache: yarn
-
- - name: Setup EAS
- uses: expo/expo-github-action@v8
- with:
- eas-version: latest
- token: ${{ secrets.EXPO_TOKEN }}
-
- - name: Install dependencies
- uses: ./.github/actions/install-deps
-
- - name: Create preview
- uses: expo/expo-github-action/preview@v8
- with:
- working-directory: apps/expo
- command: eas build --platform android --profile preview
\ No newline at end of file
diff --git a/.github/workflows/android-preview-build-local.yml b/.github/workflows/android-preview-build-local.yml
deleted file mode 100644
index a528f8e8d..000000000
--- a/.github/workflows/android-preview-build-local.yml
+++ /dev/null
@@ -1,138 +0,0 @@
-name: android-preview-build-local
-on:
- push:
- branches: ['**']
- pull_request:
- branches: ['**']
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-jobs:
- update:
- name: EAS Android Build Local
- runs-on: ubuntu-latest
- permissions:
- contents: read
- pull-requests: write
- steps:
- - name: Check for EXPO_TOKEN
- run: |
- if [ -z "${{ secrets.EXPO_TOKEN }}" ]; then
- echo "You must provide an EXPO_TOKEN secret linked to this project's Expo account in this repo's secrets."
- exit 1
- fi
-
- - name: Checkout repository
- uses: actions/checkout@v3
-
- - name: Setup Node
- uses: actions/setup-node@v3
- with:
- node-version: 20.x
- cache: yarn
-
- - name: Set up JDK 17
- uses: actions/setup-java@v3
- with:
- java-version: '17'
- distribution: 'temurin'
-
- - name: Setup EAS
- uses: expo/expo-github-action@v8
- with:
- eas-version: latest
- token: ${{ secrets.EXPO_TOKEN }}
-
- - name: Export secrets as environment variables
- env:
- JSON_SECRETS: '${{ toJSON(secrets) }}'
- run: |
- eval "$(jq -r 'to_entries | map("export \(.key)=\(.value|tostring)") | .[]' <<< "$JSON_SECRETS")"
-
- - name: Install dependencies
- uses: ./.github/actions/install-deps
-
- - name: Prebuild
- run: |
- echo "Using Mapbox Token: $MAPBOX_DOWNLOADS_TOKEN"
- export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- yarn run prebuild:expo
-
- - name: Create preview
- id: build
- run: |
- echo "Using Mapbox Token: $MAPBOX_DOWNLOADS_TOKEN"
- export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- eas build --platform android --profile preview --local
- apk_path=$(find . -name '*.apk')
- echo "APK Path: ${apk_path}"
- echo "apk_path=${apk_path}" >> $GITHUB_ENV
- working-directory: apps/expo
- env:
- DEBUG: 'true'
- continue-on-error: true
-
- - name: Upload APK
- uses: actions/upload-artifact@v3
- with:
- name: android-apk
- path: /home/runner/work/PackRat/PackRat/apps/expo/build-*.apk
-
- - name: Find or create comment
- if: github.event_name == 'pull_request'
- uses: actions/github-script@v6
- id: find_or_create_comment
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const commentIdentifier = '';
- const comments = await github.rest.issues.listComments({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- const existingComment = comments.data.find(comment => comment.body.startsWith(commentIdentifier));
-
- if (existingComment) {
- core.setOutput('comment_id', existingComment.id);
- } else {
- const commentBody = `${commentIdentifier}\nš Android APK build started... Please wait for the results! š`;
- const { data: { id: commentId } } = await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: commentBody
- });
- core.setOutput('comment_id', commentId);
- }
-
- - name: Update PR comment
- if: always() && github.event_name == 'pull_request'
- uses: actions/github-script@v6
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const commentIdentifier = '';
- const commentId = '${{ steps.find_or_create_comment.outputs.comment_id }}';
- const buildOutcome = '${{ steps.build.outcome }}';
- const buildStatus = buildOutcome == 'success' ? 'completed' : 'failed';
- const workflowUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
-
- let commentBody = `${commentIdentifier}\nAndroid APK build ${buildStatus}!`;
-
- if (buildOutcome == 'success') {
- commentBody += `\nYou can download the APK file from the following link:\n${workflowUrl}#artifacts`;
- } else {
- commentBody += '\nPlease check the workflow logs for more details on the build failure.';
- }
-
- await github.rest.issues.updateComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- comment_id: commentId,
- body: commentBody
- });
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index b21e90816..000000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: android-build-apk-gradlew
-
-on:
- push:
- branches: ['**']
- pull_request:
- branches: ['**']
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- build-android-gradlew:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
-
- - name: Setup Standard Environment
- uses: ./.github/actions/setup-standard-environment
- with:
- expo-token: ${{ secrets.EXPO_TOKEN }}
-
- - name: Prebuild
- run: |
- echo "Using Mapbox Token: $MAPBOX_DOWNLOADS_TOKEN"
- export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- yarn run prebuild:expo
-
- - name: Build Android Release
- run: |
- cd apps/expo/android && ./gradlew assembleRelease
-
- - name: Upload Artifact
- uses: actions/upload-artifact@v1
-
- with:
- name: app-release.apk
- path: android/app/build/outputs/apk/release/
diff --git a/.github/workflows/eas-build-manual.yml b/.github/workflows/eas-build-manual.yml
deleted file mode 100644
index 2e541c7b8..000000000
--- a/.github/workflows/eas-build-manual.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: EAS Build Manual
-
-on:
- workflow_dispatch:
- inputs:
- platform:
- description: 'Platform (android or ios)'
- required: true
- default: 'android'
- type: choice
- options:
- - android
- - ios
- build-type:
- description: 'Build type (preview or local)'
- required: true
- default: 'preview'
- type: choice
- options:
- - preview
- - local
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-jobs:
- update:
- name: EAS ${{ github.event.inputs.platform }} ${{ github.event.inputs.build-type }} Build
- runs-on: ubuntu-latest
- permissions:
- contents: read
- pull-requests: write
-
- steps:
- - name: Setup EAS
- uses: ./.github/actions/setup-eas
- with:
- expo-token: ${{ secrets.EXPO_TOKEN }}
- node-version: '20.x'
-
- - name: Create build
- uses: expo/expo-github-action/preview@v8
- with:
- working-directory: apps/expo
- command: |
- eas build --platform ${{ github.event.inputs.platform }} --profile preview \
- ${{ github.event.inputs.build-type == 'local' && '--local' || '' }}
\ No newline at end of file
diff --git a/.github/workflows/eas-cloud.yml b/.github/workflows/eas-cloud.yml
new file mode 100644
index 000000000..c9a0e63fe
--- /dev/null
+++ b/.github/workflows/eas-cloud.yml
@@ -0,0 +1,36 @@
+# Native production pipeline
+# Builds on EAS Cloud with auto submission
+name: EAS Build & Submit
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ paths:
+ - 'packages/app/**'
+ - 'apps/expo/**'
+ - 'packages/ui/**'
+ - 'packages/shared-types/**'
+ - 'packages/config/**'
+ - 'packages/crosspath/**'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v2
+
+ - name: Setup EAS
+ uses: ./.github/actions/setup-eas
+ with:
+ expo-token: ${{ secrets.EXPO_TOKEN }}
+
+ - name: Build on EAS
+ working-directory: ./apps/expo
+ run: eas build --platform all --profile production --non-interactive --no-wait --auto-submit
\ No newline at end of file
diff --git a/.github/workflows/expo.build.yml b/.github/workflows/eas-local.yml
similarity index 55%
rename from .github/workflows/expo.build.yml
rename to .github/workflows/eas-local.yml
index ea9210d73..fe2803618 100644
--- a/.github/workflows/expo.build.yml
+++ b/.github/workflows/eas-local.yml
@@ -1,10 +1,25 @@
name: EAS Local Build
on:
+ workflow_dispatch:
push:
branches: [ "**" ]
+ paths:
+ - 'packages/app/**'
+ - 'apps/expo/**'
+ - 'packages/ui/**'
+ - 'packages/shared-types/**'
+ - 'packages/config/**'
+ - 'packages/crosspath/**'
pull_request:
branches: [ "**" ]
+ paths:
+ - 'packages/app/**'
+ - 'apps/expo/**'
+ - 'packages/ui/**'
+ - 'packages/shared-types/**'
+ - 'packages/config/**'
+ - 'packages/crosspath/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -19,10 +34,23 @@ env: # Define a shared environment variable for android and ios build.
jobs:
build-android:
runs-on: ubuntu-latest
+
+ # will be used in find_or_create_comment
+ # and update_native_build_preview action
+ env:
+ build-name: Android APK
+ comment-unique-magic-prefix: ''
+
steps:
- name: Checkout code
uses: actions/checkout@v2
+ - name: Find or create comment for build status
+ if: github.event_name == 'pull_request'
+ uses: ./.github/actions/find-or-create-comment
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Setup Standard Environment
uses: ./.github/actions/setup-standard-environment
with:
@@ -35,6 +63,7 @@ jobs:
distribution: 'adopt'
- name: Run Local Build for Android
+ id: build
working-directory: ./apps/expo
run: |
eas build --profile preview --platform android --local --non-interactive
@@ -46,13 +75,32 @@ jobs:
with:
name: android-apk
path: /home/runner/work/PackRat/PackRat/apps/expo/build-*.apk
+
+ - name: Update build status
+ if: always() && github.event_name == 'pull_request'
+ uses: ./.github/actions/update-native-preview-build-status
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ build-outcome: ${{ steps.build.outcome }}
+
build-ios:
runs-on: macos-latest
+
+ env:
+ build-name: iOS IPA
+ comment-unique-magic-prefix: ''
+
steps:
- name: Checkout code
uses: actions/checkout@v2
+ - name: Find or create comment for build status
+ if: github.event_name == 'pull_request'
+ uses: ./.github/actions/find-or-create-comment
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+
- name: Setup Standard Environment
uses: ./.github/actions/setup-standard-environment
with:
@@ -64,6 +112,7 @@ jobs:
# xcode-version: latest-stable
- name: Run Local Build for iOS
+ id: build
working-directory: ./apps/expo
run: |
export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
@@ -75,4 +124,12 @@ jobs:
uses: actions/upload-artifact@v3
with:
name: ios-ipa
- path: /home/runner/work/PackRat/PackRat/apps/expo/build-*.ipa
+ path: /Users/runner/work/PackRat/PackRat/apps/expo/build-*.ipa
+
+ - name: Update build status
+ if: always() && github.event_name == 'pull_request'
+ uses: ./.github/actions/update-native-preview-build-status
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ build-outcome: ${{ steps.build.outcome }}
+
diff --git a/.github/workflows/ios-build-local.yml b/.github/workflows/ios-build-local.yml
deleted file mode 100644
index 5e6254dcc..000000000
--- a/.github/workflows/ios-build-local.yml
+++ /dev/null
@@ -1,116 +0,0 @@
-name: iOS App Release Build
-
-on:
- push:
- branches: ['**']
- pull_request:
- branches: ['**']
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-
-jobs:
- build:
- runs-on: macos-latest
-
- steps:
- - name: Setup repo
- uses: actions/checkout@v4
-
- - name: Setup Node
- uses: actions/setup-node@v3
- with:
- node-version: 18.x
- cache: 'yarn'
-
- - name: Setup EAS
- uses: expo/expo-github-action@v8
- with:
- eas-version: latest
- token: ${{ secrets.EXPO_TOKEN }}
-
- - name: Install dependencies
- run: yarn install
-
- - name: Setup environment variables
- run: |
- echo "MAPBOX_DOWNLOADS_TOKEN=$MAPBOX_DOWNLOADS_TOKEN" >> $GITHUB_ENV
-
- - name: Prebuild
- run: |
- echo "Using Mapbox Token: $MAPBOX_DOWNLOADS_TOKEN"
- export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- yarn run prebuild:expo
-
- - name: Build iOS app
- id: build
- run: |
- echo "Using Mapbox Token: $MAPBOX_DOWNLOADS_TOKEN"
- export MAPBOX_DOWNLOADS_TOKEN=${{ secrets.MAPBOX_DOWNLOADS_TOKEN }}
- eas build --platform ios --profile preview --local --non-interactive --output ${{ github.workspace }}/app-release.ipa
- env:
- DEBUG: 'true'
- continue-on-error: true
-
- - name: Upload IPA artifact
- uses: actions/upload-artifact@v3
- with:
- name: app-release
- path: ${{ github.workspace }}/app-release.ipa
-
- - name: Find or create comment
- if: github.event_name == 'pull_request'
- uses: actions/github-script@v6
- id: find_or_create_comment
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const commentIdentifier = '';
- const comments = await github.rest.issues.listComments({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- });
-
- const existingComment = comments.data.find(comment => comment.body.startsWith(commentIdentifier));
-
- if (existingComment) {
- core.setOutput('comment_id', existingComment.id);
- } else {
- const commentBody = `${commentIdentifier}\nš iOS app build started... Please wait for the results! š`;
- const { data: { id: commentId } } = await github.rest.issues.createComment({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- body: commentBody
- });
- core.setOutput('comment_id', commentId);
- }
- - name: Update PR comment
- if: always() && github.event_name == 'pull_request'
- uses: actions/github-script@v6
- with:
- github-token: ${{ secrets.GITHUB_TOKEN }}
- script: |
- const commentIdentifier = '';
- const commentId = '${{ steps.find_or_create_comment.outputs.comment_id }}';
- const buildOutcome = '${{ steps.build.outcome }}';
- const buildStatus = buildOutcome == 'success' ? 'completed' : 'failed';
- const workflowUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
-
- let commentBody = `${commentIdentifier}\niOS app build ${buildStatus}!`;
-
- if (buildOutcome == 'success') {
- commentBody += `\nYou can download the IPA file from the following link:\n${workflowUrl}#artifacts`;
- } else {
- commentBody += '\nPlease check the workflow logs for more details on the build failure.';
- }
-
- await github.rest.issues.updateComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- comment_id: commentId,
- body: commentBody
- });
diff --git a/apps/tauri/src/routes/__root.tsx b/apps/tauri/src/routes/__root.tsx
index 87537688b..cb8662bca 100644
--- a/apps/tauri/src/routes/__root.tsx
+++ b/apps/tauri/src/routes/__root.tsx
@@ -2,16 +2,20 @@ import React from 'react';
import { MainContentWeb } from '@packrat/ui';
import { createRootRoute, Link, Outlet } from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
-import { Navbar } from 'app/components/navigation';
+import { NavbarTauri } from 'app/components/navigation';
import { Provider } from 'app/provider';
+import { View } from 'react-native';
+import { FullSideBar } from 'app/components/navigation/SideBar';
export const Route = createRootRoute({
component: () => (
-
-
-
-
+
+
+
+
+
+
),
diff --git a/apps/tauri/src/routes/index.tsx b/apps/tauri/src/routes/index.tsx
index 97cc08a58..20942ba83 100644
--- a/apps/tauri/src/routes/index.tsx
+++ b/apps/tauri/src/routes/index.tsx
@@ -3,6 +3,7 @@ import Dashboard from 'app/screens/dashboard';
import LandingPage from 'app/components/landing_page';
import { useAuthUser } from 'app/auth/hooks';
import { createFileRoute } from '@tanstack/react-router';
+import { ScrollView } from 'react-native';
export const Route = createFileRoute('/')({
component: Home,
@@ -11,5 +12,15 @@ export const Route = createFileRoute('/')({
export default function Home() {
const user = useAuthUser();
- return <>{!user ? : }>;
+ return (
+ <>
+ {!user ? (
+
+ ) : (
+
+
+
+ )}
+ >
+ );
}
diff --git a/packages/app/components/ScoreContainer.tsx b/packages/app/components/ScoreContainer.tsx
index e2c7c97b2..ce9ed5262 100644
--- a/packages/app/components/ScoreContainer.tsx
+++ b/packages/app/components/ScoreContainer.tsx
@@ -238,7 +238,7 @@ const loadStyles = (theme: any) => {
paddingHorizontal: 25,
marginVertical: 15,
padding: 26,
- marginTop:25,
+ marginTop: 25,
// borderColor: currentTheme.colors.border,
// borderWidth: 2,
borderColor: currentTheme.colors.border,
@@ -258,7 +258,6 @@ const loadStyles = (theme: any) => {
justifyContent: 'center',
alignItems: 'flex-start',
width: '60%',
-
},
vStackXS: {
justifyContent: 'center',
@@ -280,7 +279,7 @@ const loadStyles = (theme: any) => {
},
buttonText: {
color: currentTheme.colors.white,
- fontSize: '1rem',
+ fontSize: 16,
fontWeight: '500',
},
container: {
diff --git a/packages/app/components/feedPreview/FeedPreviewCard.tsx b/packages/app/components/feedPreview/FeedPreviewCard.tsx
index 49b976c71..f3d30a82f 100644
--- a/packages/app/components/feedPreview/FeedPreviewCard.tsx
+++ b/packages/app/components/feedPreview/FeedPreviewCard.tsx
@@ -4,7 +4,7 @@ import { RLink } from '@packrat/ui';
import { LayoutChangeEvent, View } from 'react-native';
import useCustomStyles from 'app/hooks/useCustomStyles';
import loadStyles from './feedpreview.style';
-import { AntDesign, MaterialIcons } from '@expo/vector-icons';
+import { AntDesign, Fontisto, MaterialIcons } from '@expo/vector-icons';
import useTheme from 'app/hooks/useTheme';
import { useItemWeightUnit } from 'app/hooks/items';
import { convertWeight } from 'app/utils/convertWeight';
@@ -17,15 +17,24 @@ export type FeedItem = any;
interface FeedPreviewCardProps {
linkStr: string;
item: FeedItem;
+ feedType: string;
}
const RText: any = OriginalRText;
-const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
+const FeedPreviewCard: React.FC = ({
+ linkStr,
+ item,
+ feedType,
+}) => {
const { currentTheme } = useTheme();
const styles = useCustomStyles(loadStyles);
const [weightUnit] = useItemWeightUnit();
- const formattedWeight = convertWeight(item.total_weight, 'g', weightUnit);
+ const formattedWeight = convertWeight(
+ item.total_weight ?? item.weight,
+ item.unit ?? 'g',
+ weightUnit,
+ );
const [cardWidth, setCardWidth] = useState();
const handleSetCardWidth = (event: LayoutChangeEvent) => {
@@ -33,6 +42,93 @@ const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
setCardWidth(width);
};
+ if (feedType == 'similarItems') {
+ return (
+
+
+
+
+
+
+
+
+ {item.name}
+
+
+
+ {formatNumber(formattedWeight)}
+ {weightUnit}
+
+
+
+ Qty: {item.quantity}
+
+
+
+
+ {new Date(item.createdAt).toLocaleString('en-US', {
+ month: 'short',
+ day: '2-digit',
+ ...(new Date(item.createdAt).getFullYear() ==
+ new Date().getFullYear()
+ ? {}
+ : { year: 'numeric' }),
+ })}
+
+
+
+
+
+ );
+ }
+
return (
@@ -76,7 +172,7 @@ const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
>
{formatNumber(formattedWeight)}
{weightUnit}
@@ -95,7 +191,7 @@ const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
/>
{item.favorites_count}
@@ -114,7 +210,7 @@ const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
/>
{new Date(item.createdAt).toLocaleString('en-US', {
month: 'short',
@@ -128,7 +224,7 @@ const FeedPreviewCard: React.FC = ({ linkStr, item }) => {
Ttl Score: {item.total_score}
diff --git a/packages/app/components/feedPreview/index.tsx b/packages/app/components/feedPreview/index.tsx
index d4bd8fdbf..b2649ca9c 100644
--- a/packages/app/components/feedPreview/index.tsx
+++ b/packages/app/components/feedPreview/index.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import Carousel from '../carousel';
import { useFeed } from 'app/hooks/feed';
import { default as FeedPreviewCard, type FeedItem } from './FeedPreviewCard';
+import Loader from 'app/components/Loader';
interface FeedPreviewScrollProps {
itemWidth: number;
@@ -14,16 +15,18 @@ const FeedPreviewScroll: React.FC = ({
feedType,
id,
}) => {
- const { data: feedData } = useFeed({ feedType, id });
+ const { data: feedData, isLoading } = useFeed({ feedType, id });
- return (
+ return isLoading ? (
+
+ ) : (
{feedData
?.filter((item): item is FeedItem => item.type !== null)
.map((item: FeedItem) => {
const linkStr = `/${item.type}/${item.id}`;
return linkStr ? (
-
+
) : null;
})}
diff --git a/packages/app/components/navigation/Navbar/Navbar.tsx b/packages/app/components/navigation/Navbar/Navbar.tsx
index 74f1812df..3a1d2741f 100644
--- a/packages/app/components/navigation/Navbar/Navbar.tsx
+++ b/packages/app/components/navigation/Navbar/Navbar.tsx
@@ -93,7 +93,7 @@ const loadStyles = (currentTheme, isScrolled, screenWidth) => {
position: 'fixed' as 'fixed' | 'relative',
top: 0,
zIndex: 100,
- width: Platform.OS === 'web' ? '100vw' : "100%",
+ width: Platform.OS === 'web' ? '100vw' : '100%',
},
}),
},
diff --git a/packages/app/components/navigation/Navbar/NavbarTauri.tsx b/packages/app/components/navigation/Navbar/NavbarTauri.tsx
new file mode 100644
index 000000000..dec9d0800
--- /dev/null
+++ b/packages/app/components/navigation/Navbar/NavbarTauri.tsx
@@ -0,0 +1,161 @@
+import React, { useMemo } from 'react';
+import { View, Text, SafeAreaView, StyleSheet, Platform } from 'react-native';
+import { RButton, Container } from '@packrat/ui';
+import { useIsMobileView } from 'app/hooks/common';
+import { useNavigate } from 'app/hooks/navigation';
+import { NavigationList } from '../NavigationList';
+import { Drawer } from '../Drawer';
+import { useScrollTop } from 'app/hooks/common/useScrollTop';
+import { useScreenWidth } from 'app/hooks/common';
+import useTheme from 'app/hooks/useTheme';
+import { RImage } from '@packrat/ui';
+import FullSideBar from '../Sidebar/SideBar';
+
+export const NavbarTauri = () => {
+ const { currentTheme } = useTheme();
+ const scrollTop = useScrollTop();
+ const { screenWidth } = useScreenWidth();
+ const isScrolled = !!scrollTop;
+ const styles = useMemo(() => {
+ return StyleSheet.create(loadStyles(currentTheme, isScrolled, screenWidth));
+ }, [isScrolled, currentTheme, screenWidth]);
+ const navigate = useNavigate();
+
+ return (
+
+
+
+
+ {
+ navigate('/');
+ }}
+ />
+ {
+ navigate('/');
+ }}
+ >
+ PackRat
+
+
+
+
+
+
+ );
+};
+
+const NavbarStyles = {
+ floatingBg: '#0284c7',
+ floatingRadius: 25,
+ floatingBlur: 'blur(2px)',
+ transition: 'all 0.2s ease-in-out',
+ floatingSpacing: 4,
+};
+
+const loadStyles = (currentTheme, isScrolled, screenWidth) => {
+ const isWeb = Platform.OS === 'web';
+ const isFloating = isWeb && isScrolled;
+ const backgroundColor = isFloating
+ ? NavbarStyles.floatingBg
+ : currentTheme.colors.background;
+
+ return StyleSheet.create({
+ drawerStyles: {
+ backgroundColor: currentTheme.colors.background,
+ },
+ safeArea: {
+ backgroundColor,
+ width: '100%',
+ margin: 0,
+ transition: NavbarStyles.transition,
+ ...Platform.select({
+ web: {
+ ...(isFloating
+ ? {
+ backdropFilter: NavbarStyles.floatingBlur,
+ marginTop: NavbarStyles.floatingSpacing,
+ padding: NavbarStyles.floatingSpacing,
+ borderRadius: NavbarStyles.floatingRadius,
+ }
+ : {}),
+ position: 'fixed' as 'fixed' | 'relative',
+ top: 0,
+ zIndex: 100,
+ width: Platform.OS === 'web' ? '100vw' : '100%',
+ },
+ }),
+ },
+ container: {
+ width: '100vw',
+ maxWidth: '100%', // Ensure container does not exceed the viewport width
+ flex: 1, // Ensure container can grow to fit content
+ backgroundColor,
+ borderRadius: NavbarStyles.floatingRadius,
+ flexDirection: 'row', // Keep flexDirection as row for alignment
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ flexWrap: 'wrap', // Allow items to wrap
+ height: 60, // Ensure container takes full height of its container
+ padding: 16,
+ },
+ header: {
+ flexDirection: 'row', // Keep flexDirection as row for initial alignment
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ width: '100%', // Ensure header takes full width of its container
+ flexWrap: 'wrap', // Allow header items to wrap
+ },
+ logoContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ },
+ logo: {
+ marginRight: 10,
+ cursor: 'pointer',
+ },
+ logoText: {
+ color: currentTheme.colors.text,
+ fontSize: 38,
+ fontWeight: '900',
+ cursor: 'pointer',
+ },
+ menuBar: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'flex-end',
+ paddingHorizontal: 16,
+ flex: 1, // Keep flexible but consider its behavior with wrapping,
+ flexWrap: 'wrap', // Allow items to wrap
+ },
+ drawerTrigger: {},
+ menuBarItemActive: {
+ // Apply styles for the active item
+ // ...
+ },
+ menuBarItemTextActive: {
+ // Apply styles for the active item's text
+ // ...
+ },
+ menuBarItemSelected: {
+ // Apply styles for the selected item
+ // ...
+ },
+ menuBarItemTextSelected: {
+ // Apply styles for the selected item's text
+ // ...
+ },
+ });
+};
diff --git a/packages/app/components/navigation/Navbar/index.ts b/packages/app/components/navigation/Navbar/index.ts
index ec1cfd76d..00bf99a9a 100644
--- a/packages/app/components/navigation/Navbar/index.ts
+++ b/packages/app/components/navigation/Navbar/index.ts
@@ -1 +1,2 @@
export { Navbar } from './Navbar';
+export { NavbarTauri } from './NavbarTauri';
diff --git a/packages/app/components/navigation/Sidebar/ProfileNavigationList.tsx b/packages/app/components/navigation/Sidebar/ProfileNavigationList.tsx
new file mode 100644
index 000000000..003dd5d8c
--- /dev/null
+++ b/packages/app/components/navigation/Sidebar/ProfileNavigationList.tsx
@@ -0,0 +1,56 @@
+import React from 'react';
+import { useNavigationList } from 'app/hooks/navigation';
+import { NavigationItem } from '../NavigationItem';
+import { useIsMobileView } from 'app/hooks/common';
+import { View } from 'tamagui';
+import useTheme from '../../../hooks/useTheme';
+
+interface ProfileNavigationList {
+ itemStyle?: any;
+ onItemSelect?: (item: any) => void;
+}
+
+export const ProfileNavigationList = ({ itemStyle, onItemSelect }) => {
+ const isMobileView = useIsMobileView();
+ const { currentTheme } = useTheme();
+ const { navigationItems } = useNavigationList();
+
+ return (
+ <>
+ {navigationItems
+ ?.filter(({ href }) => href === '/profile' || href === '/logout')
+ .map(({ type, ...Item }, index) => {
+ const item = Item as any;
+ console.log(item);
+
+ return (
+
+ {type === 'link' ? (
+
+ ) : (
+ item.Component &&
+ )}
+
+ );
+ })}
+ >
+ );
+};
diff --git a/packages/app/components/navigation/Sidebar/SideBar.tsx b/packages/app/components/navigation/Sidebar/SideBar.tsx
new file mode 100644
index 000000000..3336f10eb
--- /dev/null
+++ b/packages/app/components/navigation/Sidebar/SideBar.tsx
@@ -0,0 +1,99 @@
+import { useState } from 'react';
+import { View, useMedia, styled } from 'tamagui';
+import { RIconButton } from '@packrat/ui';
+import useTheme from 'app/hooks/useTheme';
+import Ionicons from '@expo/vector-icons/Ionicons';
+import { SidebarNavigationList } from './SidebarNavigationList';
+import { ProfileNavigationList } from './ProfileNavigationList';
+
+export function FullSideBar() {
+ const [openDrawer, setOpenDrawer] = useState(false);
+ const { sm } = useMedia();
+
+ return (
+
+ {!sm && }
+ {sm && }
+
+
+ );
+}
+
+FullSideBar.fileName = 'FullSideBar';
+
+function Sidebar() {
+ const { currentTheme } = useTheme();
+
+ return (
+
+ {}}
+ />
+
+ );
+}
+
+function ProfileDrawer({
+ open,
+ setOpen,
+ onItemSelect,
+}: {
+ open: boolean;
+ setOpen: (open: boolean) => void;
+ onItemSelect: (item: any) => void;
+}) {
+ const { currentTheme } = useTheme();
+
+ return (
+
+ }
+ onPress={() => setOpen(!open)}
+ />
+ {open && (
+
+
+
+ )}
+
+ );
+}
+
+export default FullSideBar;
diff --git a/packages/app/components/navigation/Sidebar/SidebarNavigationList.tsx b/packages/app/components/navigation/Sidebar/SidebarNavigationList.tsx
new file mode 100644
index 000000000..e2a396070
--- /dev/null
+++ b/packages/app/components/navigation/Sidebar/SidebarNavigationList.tsx
@@ -0,0 +1,57 @@
+import React from 'react';
+import { useNavigationList } from 'app/hooks/navigation';
+import { NavigationItem } from '../NavigationItem';
+import { useIsMobileView } from 'app/hooks/common';
+import { View } from 'tamagui';
+import useTheme from '../../../hooks/useTheme';
+
+interface SidebarNavigationList {
+ itemStyle?: any;
+ onItemSelect?: (item: any) => void;
+}
+
+export const SidebarNavigationList = ({ itemStyle, onItemSelect }) => {
+ const isMobileView = useIsMobileView();
+ const { currentTheme } = useTheme();
+ const { navigationItems } = useNavigationList();
+
+ return (
+ <>
+ {navigationItems
+ ?.filter(
+ ({ href, type }) =>
+ href !== '/profile' && href !== '/logout' && type !== 'divider',
+ )
+ .map(({ type, ...Item }, index) => {
+ const item = Item as any;
+ return (
+
+ {type === 'link' ? (
+
+ ) : (
+ item.Component &&
+ )}
+
+ );
+ })}
+ >
+ );
+};
diff --git a/packages/app/hooks/feed/index.ts b/packages/app/hooks/feed/index.ts
index f588cb888..636e8cfaa 100644
--- a/packages/app/hooks/feed/index.ts
+++ b/packages/app/hooks/feed/index.ts
@@ -1,7 +1,8 @@
import { usePublicFeed } from './publicFeed';
import { useUserPacks } from './../packs';
import { useUserTrips } from '../singletrips';
-import { useSimilarPacks } from 'app/hooks/packs/useSimilarPacks';
+import { useSimilarPacks } from 'app/hooks/packs';
+import { useSimilarItems } from 'app/hooks/items';
export const useFeed = ({
queryString = 'Most Recent',
@@ -25,6 +26,8 @@ export const useFeed = ({
return useUserTrips(ownerId || undefined);
case 'similarPacks':
return useSimilarPacks(id);
+ case 'similarItems':
+ return useSimilarItems(id);
default:
return { data: null, error: null, isLoading: true };
}
diff --git a/packages/app/hooks/items/index.ts b/packages/app/hooks/items/index.ts
index 2512212e1..1244bc362 100644
--- a/packages/app/hooks/items/index.ts
+++ b/packages/app/hooks/items/index.ts
@@ -5,3 +5,4 @@ export { useItemsUpdater } from './useItemsUpdater';
export { useItemWeightUnit } from './useItemWeightUnit';
export { useItemId } from './useItemId';
export { useItem } from './useItem';
+export { useSimilarItems } from './useSimilarItems';
diff --git a/packages/app/hooks/items/useSimilarItems.ts b/packages/app/hooks/items/useSimilarItems.ts
new file mode 100644
index 000000000..2b8db4967
--- /dev/null
+++ b/packages/app/hooks/items/useSimilarItems.ts
@@ -0,0 +1,13 @@
+import { queryTrpc } from '../../trpc';
+
+export const useSimilarItems = (id: string) => {
+ const { data, error, isLoading, refetch } =
+ queryTrpc.getSimilarItems.useQuery(
+ { id, limit: 10 },
+ {
+ refetchOnWindowFocus: true,
+ },
+ );
+
+ return { data, error, isLoading, refetch };
+};
diff --git a/packages/app/screens/items/item-details.tsx b/packages/app/screens/items/item-details.tsx
index e16046de5..d49ebdd7f 100644
--- a/packages/app/screens/items/item-details.tsx
+++ b/packages/app/screens/items/item-details.tsx
@@ -10,6 +10,7 @@ import { usePagination } from 'app/hooks/common';
import {
BaseModal,
DropdownComponent,
+ RH3,
RImage,
RScrollView,
RStack,
@@ -18,16 +19,20 @@ import {
} from '@packrat/ui';
import useResponsive from 'app/hooks/useResponsive';
import { CustomCard } from 'app/components/card';
+import LargeCard from 'app/components/card/LargeCard';
+import FeedPreview from 'app/components/feedPreview';
export default function ItemDetails() {
const { limit, handleLimitChange, page, handlePageChange } = usePagination();
const [itemId] = useItemId();
const { data: item, isError } = useItem(itemId);
const styles = useCustomStyles(loadStyles);
+ const { currentTheme } = useTheme();
+
console.log({ item, itemId });
return (
-
+
{!isError && item && (
@@ -55,6 +60,26 @@ export default function ItemDetails() {
}
type="item"
/>
+
+
+ Similar Items
+
+
+
)}