diff --git a/.circleci/config.yml b/.circleci/config.yml
index 538b4b8d92..a945d51a1a 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -280,10 +280,12 @@ jobs:
name: Set branch environment
command: |
echo 'export VITE_PROJECT_VERSION=${CIRCLE_SHA1}' >> $BASH_ENV
+ echo 'export VITE_SITE_VARIANT=test-ci' >> $BASH_ENV
- run:
name: Check environment variables
command: |
echo $VITE_PROJECT_VERSION
+ echo $VITE_SITE_VARIANT
- run:
command: yarn build
- persist_to_workspace:
@@ -356,7 +358,7 @@ jobs:
--build-secret VITE_SENTRY_DSN="$VITE_SENTRY_DSN" \
--build-secret VITE_GA_TRACKING_ID="$VITE_GA_TRACKING_ID" \
--build-secret VITE_PATREON_CLIENT_ID="$VITE_PATREON_CLIENT_ID" \
- --build-secret VITE_PLATFORM_THEME="$VITE_PLATFORM_THEME" \
+ --build-secret VITE_PLATFORM_PROFILES="$VITE_PLATFORM_PROFILES" \
--build-secret VITE_PROJECT_VERSION="$VITE_PROJECT_VERSION" \
--build-secret VITE_SUPPORTED_MODULES="$VITE_SUPPORTED_MODULES" \
--build-secret VITE_ACADEMY_RESOURCE="$VITE_ACADEMY_RESOURCE" \
@@ -401,6 +403,7 @@ jobs:
- run:
command: npm run test ci prod
environment:
+ VITE_SITE_VARIANT: test-ci
CI_BROWSER: << parameters.CI_BROWSER >>
CI_NODE: << parameters.CI_NODE >>
CI_GROUP: ci-<< parameters.CI_BROWSER >>
diff --git a/.env b/.env
index 315ce51557..c24465c878 100644
--- a/.env
+++ b/.env
@@ -1,9 +1,10 @@
### Prefix VITE_ to use client-side (only for non-sensitive data!)
PORT=3000
WS_URLS=localhost:24678,ws://localhost:24678
+
+# All valid options for VITE_THEME: project-kamp, fixing-fashion or precious-plastic
VITE_THEME=precious-plastic
-# VITE_THEME=fixing-fashion
-# VITE_THEME=project-kamp
+
VITE_ACADEMY_RESOURCE=https://onearmy.github.io/academy/
# VITE_ACADEMY_RESOURCE=https://project-kamp-academy.netlify.app/
# VITE_ACADEMY_RESOURCE=https://fixing-fashion-academy.netlify.app/
@@ -17,4 +18,8 @@ VITE_COMMUNITY_PROGRAM_URL=https://community.preciousplastic.com/academy/guides/
VITE_PROFILE_GUIDELINES_URL=https://community.preciousplastic.com/academy/guides/platform
# VITE_PROFILE_GUIDELINES_URL=https://drive.google.com/file/d/1fXTtBbzgCO0EL6G9__aixwqc-Euqgqnd/view
# VITE_PROFILE_GUIDELINES_URL=https://community.fixing.fashion/academy/guides/profile
-VITE_QUESTIONS_GUIDELINES_URL=https://community.preciousplastic.com/academy/guides/guidelines-questions
\ No newline at end of file
+VITE_QUESTIONS_GUIDELINES_URL=https://community.preciousplastic.com/academy/guides/guidelines-questions
+
+# For testing, VITE_PLATFORM_PROFILES in localStorage is prioritised over this value
+# All valid options for VITE_PLATFORM_PROFILES: "member,workspace,community-builder,space,collection-point,machine-builder"
+VITE_PLATFORM_PROFILES="member,workspace,community-builder,collection-point,machine-builder"
diff --git a/.github/actions/destroy-fly-preview-app/Dockerfile b/.github/actions/destroy-fly-preview-app/Dockerfile
new file mode 100644
index 0000000000..1c8e77415b
--- /dev/null
+++ b/.github/actions/destroy-fly-preview-app/Dockerfile
@@ -0,0 +1,11 @@
+FROM alpine
+
+RUN apk add --no-cache curl jq
+
+RUN curl -L https://fly.io/install.sh | FLYCTL_INSTALL=/usr/local sh
+
+COPY entrypoint.sh /entrypoint.sh
+
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
\ No newline at end of file
diff --git a/.github/actions/destroy-fly-preview-app/action.yml b/.github/actions/destroy-fly-preview-app/action.yml
new file mode 100644
index 0000000000..b833f222c4
--- /dev/null
+++ b/.github/actions/destroy-fly-preview-app/action.yml
@@ -0,0 +1,12 @@
+name: "Destroy fly.io app"
+description: "Destroy fly.io app if matching app name found"
+author: iSCJT
+branding:
+ icon: "delete"
+ color: "red"
+runs:
+ using: "docker"
+ image: "Dockerfile"
+inputs:
+ name:
+ description: Fly app name
\ No newline at end of file
diff --git a/.github/actions/destroy-fly-preview-app/entrypoint.sh b/.github/actions/destroy-fly-preview-app/entrypoint.sh
new file mode 100644
index 0000000000..aa4b5696ee
--- /dev/null
+++ b/.github/actions/destroy-fly-preview-app/entrypoint.sh
@@ -0,0 +1,16 @@
+#!/bin/sh -l
+
+set -ex
+
+# Change underscores to hyphens.
+app="${INPUT_NAME//_/-}"
+
+
+if ! flyctl status --app "$app"; then
+ echo "App name not found"
+ exit 1
+fi
+
+flyctl apps destroy "$app" -y
+echo "App $app successfully destroyed"
+exit 0
\ No newline at end of file
diff --git a/.github/workflows/fly-pr-preview.yml b/.github/workflows/pr-preview-fly-deploy.yml
similarity index 90%
rename from .github/workflows/fly-pr-preview.yml
rename to .github/workflows/pr-preview-fly-deploy.yml
index 780c4a678b..f5eb55fe49 100644
--- a/.github/workflows/fly-pr-preview.yml
+++ b/.github/workflows/pr-preview-fly-deploy.yml
@@ -1,4 +1,4 @@
-name: Fly PR Preview
+name: Deploy Fly PR Preview
on:
# Run this workflow on every PR event. Existing review apps will be updated when the PR is updated.
pull_request_target:
@@ -16,7 +16,7 @@ on:
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
# Set these to your Fly.io organization and preferred region.
- FLY_REGION: cdg
+ FLY_REGION: ams
FLY_ORG: one-army
jobs:
@@ -45,8 +45,7 @@ jobs:
- name: Deploy PR app to Fly.io
id: deploy
- uses: superfly/fly-pr-review-apps@1.2.1
+ uses: superfly/fly-pr-review-apps@1.3.0
with:
config: fly-preview.toml
- name: community-platform-pr-${{ github.event.number }}
- secrets: VITE_SITE_VARIANT=preview VITE_PROJECT_VERSION=${GITHUB_SHA}
+ name: community-platform-pr-${{ github.event.number }}
\ No newline at end of file
diff --git a/.github/workflows/pr-preview-fly-destroy.yml b/.github/workflows/pr-preview-fly-destroy.yml
new file mode 100644
index 0000000000..01820999bb
--- /dev/null
+++ b/.github/workflows/pr-preview-fly-destroy.yml
@@ -0,0 +1,29 @@
+name: Destroy Fly PR Preview
+
+on:
+ pull_request_target:
+ types:
+ - unlabeled
+
+env:
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+
+jobs:
+ label_removed:
+ if: github.event.label.name == 'Review allow-preview ✅'
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ concurrency:
+ group: pr-${{ github.event.number }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ # pull the repo from the pull request source, not the default local repo
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Destroy fly.io preview app
+ id: destroy
+ uses: ./.github/actions/destroy-fly-preview-app
+ with:
+ name: community-platform-pr-${{ github.event.number }}
\ No newline at end of file
diff --git a/.github/workflows/pr-preview-remove-label.yml b/.github/workflows/pr-preview-remove-label.yml
new file mode 100644
index 0000000000..ff399a5c48
--- /dev/null
+++ b/.github/workflows/pr-preview-remove-label.yml
@@ -0,0 +1,35 @@
+name: Remove PR Preview Label
+on:
+ # Run this workflow on every PR event. Existing review apps will be updated when the PR is updated.
+ pull_request_target:
+ # Trigger when labels are changed or more commits added to a PR that contains labels
+ types: [closed]
+
+jobs:
+ preview_app:
+ if: contains(github.event.pull_request.labels.*.name, 'Review allow-preview ✅')
+ runs-on: ubuntu-latest
+ continue-on-error: true
+ # Only run one deployment at a time per PR.
+ concurrency:
+ group: pr-${{ github.event.number }}
+
+ steps:
+ - name: Get code
+ uses: actions/checkout@v4
+ with:
+ # pull the repo from the pull request source, not the default local repo
+ ref: ${{ github.event.pull_request.head.sha }}
+
+ - name: Remove preview label
+ uses: actions/github-script@v7
+ with:
+ script: |
+ await github.rest.issues.removeLabel({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ issue_number: github.event.number,
+ name: 'Review allow-preview ✅',
+ });
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/.github/workflows/pr-preview.yml b/.github/workflows/pr-preview.yml
deleted file mode 100644
index 7b081f5db2..0000000000
--- a/.github/workflows/pr-preview.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-# Create and deploy a build on all pull requests
-name: PR Preview
-on:
- # use pull_request_target so that secrets accessible from fork
- pull_request_target:
- # Trigger when labels are changed or more commits added to a PR that contains labels
- types: [labeled, synchronize]
- # Only create a preview if changes have been made to the main src code or backend functions
- paths:
- - 'src/**'
- - 'functions/**'
- - 'packages/components/**'
- - '.github/workflows/pr-preview.yml'
- - 'package.json'
- - 'yarn.lock'
-
-jobs:
- build_and_preview:
- # NOTE - as we are going to check out and build from forks we also need to add manual
- # check that malicious code has not been inserted. This is handled by manually labelling the PR
- # https://securitylab.github.com/research/github-actions-preventing-pwn-requests
- if: contains(github.event.pull_request.labels.*.name, 'Review allow-preview ✅')
- runs-on: ubuntu-latest
- continue-on-error: true
- steps:
- - uses: actions/checkout@v2
- with:
- # pull the repo from the pull request source, not the default local repo
- ref: ${{ github.event.pull_request.head.sha }}
- - uses: actions/setup-node@v3
- with:
- node-version: '18'
- - name: Get yarn cache directory path
- id: yarn-cache-dir-path
- run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
- # Setup yarn 2 cache: https://github.com/actions/cache/blob/main/examples.md#node---yarn
- - name: Setup Cache
- uses: actions/cache@v2
- id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
- with:
- path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
- key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
- restore-keys: |
- ${{ runner.os }}-yarn-
- - name: Install npm dependencies
- run: yarn install --immutable
- - name: Set environment variables
- run: export VITE_PROJECT_VERSION=${GITHUB_SHA}
- - name: Check environment variables
- run: echo $VITE_PROJECT_VERSION
- - name: Build for Preview
- run: npm run build
- env:
- # currently some linting fails when CI mode enabled (warnings become errors)
- # disable until fully resolved
- CI: false
- # specify the 'preview' site variant to populate the relevant firebase config
- VITE_SITE_VARIANT: preview
- # The hosting-deploy action calls firebase tools via npx, however installing globally
- # gives us control over what version will be made available
- - name: Install firebase-tools globally
- run: npm i -g firebase-tools
- - uses: FirebaseExtended/action-hosting-deploy@v0
- with:
- repoToken: '${{ secrets.GITHUB_TOKEN }}'
- # the details of the service account need to be populated to github secrets
- # these must match the target projectId account
- firebaseServiceAccount: '${{ secrets.ONEARMY_NEXT_FIREBASE_SERVICE_ACCOUNT }}'
- expires: 30d
- projectId: onearmy-next
diff --git a/Dockerfile b/Dockerfile
index 726b69d797..81f4dfff6e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -27,7 +27,7 @@ ARG VITE_FIREBASE_STORAGE_BUCKET
ARG VITE_SENTRY_DSN
ARG VITE_GA_TRACKING_ID
ARG VITE_PATREON_CLIENT_ID
-ARG VITE_PLATFORM_THEME
+ARG VITE_PLATFORM_PROFILES
ARG VITE_PROJECT_VERSION
ARG VITE_SUPPORTED_MODULES
ARG VITE_ACADEMY_RESOURCE
@@ -66,12 +66,12 @@ RUN --mount=type=secret,id=VITE_BRANCH \
--mount=type=secret,id=VITE_SENTRY_DSN \
--mount=type=secret,id=VITE_GA_TRACKING_ID \
--mount=type=secret,id=VITE_PATREON_CLIENT_ID \
- --mount=type=secret,id=VITE_PLATFORM_THEME \
--mount=type=secret,id=VITE_PROJECT_VERSION \
--mount=type=secret,id=VITE_SUPPORTED_MODULES \
--mount=type=secret,id=VITE_ACADEMY_RESOURCE \
--mount=type=secret,id=VITE_API_URL \
--mount=type=secret,id=VITE_PROFILE_GUIDELINES_URL \
+ --mount=type=secret,id=VITE_PLATFORM_PROFILES \
--mount=type=secret,id=VITE_SITE_NAME \
--mount=type=secret,id=VITE_THEME \
--mount=type=secret,id=VITE_DONATIONS_BODY \
@@ -91,7 +91,7 @@ RUN --mount=type=secret,id=VITE_BRANCH \
VITE_SENTRY_DSN="$(cat /run/secrets/VITE_SENTRY_DSN)" && \
VITE_GA_TRACKING_ID="$(cat /run/secrets/VITE_GA_TRACKING_ID)" && \
VITE_PATREON_CLIENT_ID="$(cat /run/secrets/VITE_PATREON_CLIENT_ID)" && \
- VITE_PLATFORM_THEME="$(cat /run/secrets/VITE_PLATFORM_THEME)" && \
+ VITE_PLATFORM_PROFILES="$(cat /run/secrets/VITE_PLATFORM_PROFILES)" && \
VITE_PROJECT_VERSION="$(cat /run/secrets/VITE_PROJECT_VERSION)" && \
VITE_SUPPORTED_MODULES="$(cat /run/secrets/VITE_SUPPORTED_MODULES)" && \
VITE_ACADEMY_RESOURCE="$(cat /run/secrets/VITE_ACADEMY_RESOURCE)" && \
@@ -116,7 +116,7 @@ RUN --mount=type=secret,id=VITE_BRANCH \
echo "VITE_SENTRY_DSN=\"${VITE_SENTRY_DSN}\"" >> .env && \
echo "VITE_GA_TRACKING_ID=\"${VITE_GA_TRACKING_ID}\"" >> .env && \
echo "VITE_PATREON_CLIENT_ID=\"${VITE_PATREON_CLIENT_ID}\"" >> .env && \
- echo "VITE_PLATFORM_THEME=\"${VITE_PLATFORM_THEME}\"" >> .env && \
+ echo "VITE_PLATFORM_PROFILES=\"${VITE_PLATFORM_PROFILES}\"" >> .env && \
echo "VITE_PROJECT_VERSION=\"${VITE_PROJECT_VERSION}\"" >> .env && \
echo "VITE_SUPPORTED_MODULES=\"${VITE_SUPPORTED_MODULES}\"" >> .env && \
echo "VITE_ACADEMY_RESOURCE=\"${VITE_ACADEMY_RESOURCE}\"" >> .env && \
diff --git a/Dockerfile.preview b/Dockerfile.preview
new file mode 100644
index 0000000000..b22547dc0e
--- /dev/null
+++ b/Dockerfile.preview
@@ -0,0 +1,49 @@
+# syntax = docker/dockerfile:1
+
+FROM node:20-slim AS base
+
+LABEL fly_launch_runtime="Remix"
+
+# Remix app lives here
+WORKDIR /app
+
+# Set production environment
+ENV NODE_ENV="production"
+ARG YARN_VERSION=3.6.4
+
+# Install Yarn 3
+RUN corepack enable && \
+ yarn set version ${YARN_VERSION}
+
+# Throw-away build stage to reduce size of final image
+FROM base AS build
+
+# Install packages needed to build node modules
+RUN apt-get update -qq && \
+ apt-get install --no-install-recommends -y build-essential node-gyp pkg-config python-is-python3
+
+# Copy source code
+COPY . .
+
+# Install packages
+RUN yarn install
+
+RUN echo "VITE_SITE_VARIANT=\"preview\"" >> .env && \
+ echo "VITE_GA_TRACKING_ID=\"G-G4QQ9NSQPC\"" >> .env && \
+ echo "VITE_PATREON_CLIENT_ID=\"RLgZo7SKNVn8cOyFlYeGZsNnoCOjvzQiNJFskDfoxltZltjPPGNMPokwJckHNphU\"" >> .env && \
+ echo "VITE_THEME=\"precious-plastic\"" >> .env && \
+ echo "VITE_PLATFORM_PROFILES=\"member,workspace,community-builder,collection-point,machine-builder\"" >> .env
+
+# Build application
+RUN yarn run build
+
+# Final stage for app image
+FROM base
+
+# Copy built application
+COPY --from=build /app /app
+
+# Start the server by default, this can be overwritten at runtime
+EXPOSE 3000
+CMD [ "yarn", "run", "start" ]
+
diff --git a/fly-ff.toml b/fly-ff.toml
index 0d74e09125..03bd8c5916 100644
--- a/fly-ff.toml
+++ b/fly-ff.toml
@@ -1,18 +1,19 @@
app = 'community-platform-ff'
-primary_region = 'cdg'
+primary_region = 'ams'
[build]
[http_service]
- internal_port = 3000
- force_https = true
- auto_stop_machines = 'off'
- processes = ['app']
+internal_port = 3000
+force_https = true
+auto_stop_machines = 'suspend'
+min_machines_running = 1
+processes = ['app']
[env]
- VITE_BRANCH = "production"
+VITE_BRANCH = "production"
[[vm]]
- memory = '4gb'
- cpu_kind = 'shared'
- cpus = 4
+memory = '4gb'
+cpu_kind = 'shared'
+cpus = 4
diff --git a/fly-pk.toml b/fly-pk.toml
index e54fc228ce..851e0ed699 100644
--- a/fly-pk.toml
+++ b/fly-pk.toml
@@ -1,18 +1,18 @@
app = 'community-platform-pk'
-primary_region = 'cdg'
+primary_region = 'ams'
[build]
[http_service]
- internal_port = 3000
- force_https = true
- auto_stop_machines = 'off'
- processes = ['app']
+internal_port = 3000
+force_https = true
+auto_stop_machines = 'off'
+processes = ['app']
[env]
- VITE_BRANCH = "production"
+VITE_BRANCH = "production"
[[vm]]
- memory = '4gb'
- cpu_kind = 'shared'
- cpus = 4
+memory = '4gb'
+cpu_kind = 'shared'
+cpus = 4
diff --git a/fly-pp.toml b/fly-pp.toml
index cdd0b52960..dc73c6e054 100644
--- a/fly-pp.toml
+++ b/fly-pp.toml
@@ -1,18 +1,18 @@
app = 'community-platform-pp'
-primary_region = 'cdg'
+primary_region = 'ams'
[build]
[http_service]
- internal_port = 3000
- force_https = true
- auto_stop_machines = 'off'
- processes = ['app']
+internal_port = 3000
+force_https = true
+auto_stop_machines = 'off'
+processes = ['app']
[env]
- VITE_BRANCH = "production"
+VITE_BRANCH = "production"
[[vm]]
- memory = '4gb'
- cpu_kind = 'shared'
- cpus = 4
+memory = '4gb'
+cpu_kind = 'shared'
+cpus = 4
diff --git a/fly-preview.toml b/fly-preview.toml
index 45717ddb53..07ae141679 100644
--- a/fly-preview.toml
+++ b/fly-preview.toml
@@ -1,10 +1,18 @@
+primary_region = 'ams'
+
[build]
+dockerfile = "Dockerfile.preview"
[http_service]
internal_port = 3000
force_https = true
-auto_stop_machines = 'off'
+auto_stop_machines = 'stop'
processes = ['app']
[env]
VITE_BRANCH = "preview"
+
+[[vm]]
+memory = '4gb'
+cpu_kind = 'shared'
+cpus = 2
diff --git a/functions/src/userUpdates/index.ts b/functions/src/userUpdates/index.ts
index d69b819e85..6ff3ce6e11 100644
--- a/functions/src/userUpdates/index.ts
+++ b/functions/src/userUpdates/index.ts
@@ -6,8 +6,8 @@ import { backupUser } from './backupUser'
import { updateDiscussionComments } from './updateDiscussionComments'
import { updateMapPins } from './updateMapPins'
+import type { IUserDB } from 'oa-shared/models/user'
import type { IDBDocChange } from '../models'
-import { IUserDB } from 'oa-shared/models/user'
/*********************************************************************
* Side-effects to be carried out on various user updates, namely:
diff --git a/functions/src/userUpdates/updateDiscussionComments.test.ts b/functions/src/userUpdates/updateDiscussionComments.test.ts
index f676726fb9..0c5c5b8e4e 100644
--- a/functions/src/userUpdates/updateDiscussionComments.test.ts
+++ b/functions/src/userUpdates/updateDiscussionComments.test.ts
@@ -1,6 +1,7 @@
-import { IUserDB } from 'oa-shared/models/user'
import { updateDiscussionComments } from './updateDiscussionComments'
+import type { IUserDB } from 'oa-shared/models/user'
+
const prevUser = {
_id: 'hjg235z',
location: { countryCode: 'UK' },
diff --git a/functions/src/userUpdates/updateMapPins.ts b/functions/src/userUpdates/updateMapPins.ts
index a8c85d18e5..7d769f26af 100644
--- a/functions/src/userUpdates/updateMapPins.ts
+++ b/functions/src/userUpdates/updateMapPins.ts
@@ -4,6 +4,7 @@ import { db } from '../Firebase/firestoreDB'
import {
getCreatorImage,
getFirstCoverImage,
+ getValidTags,
hasDetailsForMapPinChanged,
} from './utils'
@@ -32,14 +33,15 @@ export const updateMapPins = async (prevUser: IUserDB, user: IUserDB) => {
coverImages,
displayName,
isContactableByPublic,
+ location,
profileType,
- workspaceType,
userImage,
- location,
+ workspaceType,
} = user
const creatorImage = getCreatorImage(userImage)
const coverImage = getFirstCoverImage(coverImages)
const countryCode = location?.countryCode || country || ''
+ const tags = user.tags ? getValidTags(user.tags) : []
const creator = {
_lastActive,
@@ -50,8 +52,9 @@ export const updateMapPins = async (prevUser: IUserDB, user: IUserDB) => {
displayName,
isContactableByPublic,
profileType,
- workspaceType,
+ tags,
userImage: creatorImage,
+ workspaceType,
}
// Only one expected
diff --git a/functions/src/userUpdates/utils.test.ts b/functions/src/userUpdates/utils.test.ts
index 963c5d33a6..7d44f6597c 100644
--- a/functions/src/userUpdates/utils.test.ts
+++ b/functions/src/userUpdates/utils.test.ts
@@ -4,21 +4,43 @@ import {
hasDetailsForMapPinChanged,
hasLocationDetailsChanged,
hasUserImageChanged,
+ hasUserTagsChanged,
} from './utils'
+import type { IUploadedFileMeta } from 'oa-shared'
import type { IUserDB } from 'oa-shared/models/user'
+const unimportantUserDetails = {
+ _authID: '00',
+ _id: 'unchangeable',
+ _created: '',
+ _deleted: false,
+ userName: 'unchangeable',
+ verified: false,
+ coverImages: [],
+ links: [],
+}
+
describe('hasDetailsChanged', () => {
it("returns false for every field that's the same", () => {
const user = {
+ _lastActive: 'same',
+ about: 'about',
displayName: 'same',
+ isContactableByPublic: true,
+ profileType: 'member',
userImage: {
downloadUrl: 'https://more.same/image.jpg',
- },
+ } as IUploadedFileMeta,
badges: {
verified: true,
supporter: false,
},
+ tags: {
+ hguowewer: true,
+ '76khbrw': false,
+ },
+ ...unimportantUserDetails,
} as IUserDB
expect(hasDetailsChanged(user, user)).toEqual([false, false, false, false])
@@ -26,25 +48,42 @@ describe('hasDetailsChanged', () => {
it("returns true for every field that's different", () => {
const prevUser = {
+ _lastActive: 'yesterday',
+ about: 'Old about',
displayName: 'old',
+ profileType: 'member',
+ workspaceType: null,
userImage: {
downloadUrl: 'https://more.old/image.jpg',
- },
+ } as IUploadedFileMeta,
badges: {
verified: true,
supporter: true,
},
+ tags: {
+ hguowewer: true,
+ },
+ ...unimportantUserDetails,
} as IUserDB
const user = {
+ _lastActive: 'today',
+ about: 'New about description.',
displayName: 'new',
+ profileType: 'space',
+ workspaceType: 'extrusion',
userImage: {
downloadUrl: 'https://more.new/image.jpg',
- },
+ } as IUploadedFileMeta,
badges: {
verified: false,
supporter: false,
},
+ tags: {
+ hguowewer: true,
+ '76khbrw': true,
+ },
+ ...unimportantUserDetails,
} as IUserDB
expect(hasDetailsChanged(prevUser, user)).toEqual([true, true, true, true])
@@ -99,7 +138,6 @@ describe('hasDetailsForCommentsChanged', () => {
userImage: {
downloadUrl: 'http://etc.',
},
-
badges: {
verified: true,
supporter: false,
@@ -196,3 +234,61 @@ describe('hasUserImageChanged', () => {
expect(hasUserImageChanged(prevUser, user)).toEqual(true)
})
})
+
+describe('hasUserTagsChanged', () => {
+ it('returns false when nothing has changed', () => {
+ const user = {
+ displayName: 'displayName',
+ profileType: 'member',
+ tags: {
+ gyi: false,
+ bnhjo: true,
+ },
+ ...unimportantUserDetails,
+ } as IUserDB
+ expect(hasUserTagsChanged(user, user)).toEqual(false)
+ })
+
+ it('returns true when a tag is added', () => {
+ const prevUser = {
+ displayName: 'displayName',
+ profileType: 'member',
+ tags: {
+ gyi: false,
+ },
+ ...unimportantUserDetails,
+ } as IUserDB
+
+ const user = {
+ displayName: 'displayName',
+ profileType: 'member',
+ tags: {
+ gyi: false,
+ bnhjo: true,
+ },
+ ...unimportantUserDetails,
+ } as IUserDB
+ expect(hasUserTagsChanged(prevUser, user)).toEqual(true)
+ })
+
+ it('returns true when a tag is changed', () => {
+ const prevUser = {
+ displayName: 'displayName',
+ profileType: 'member',
+ tags: {
+ gyi: false,
+ },
+ ...unimportantUserDetails,
+ } as IUserDB
+
+ const user = {
+ displayName: 'displayName',
+ profileType: 'member',
+ tags: {
+ gyi: true,
+ },
+ ...unimportantUserDetails,
+ } as IUserDB
+ expect(hasUserTagsChanged(prevUser, user)).toEqual(true)
+ })
+})
diff --git a/functions/src/userUpdates/utils.ts b/functions/src/userUpdates/utils.ts
index 69415eff65..5515ca7fbb 100644
--- a/functions/src/userUpdates/utils.ts
+++ b/functions/src/userUpdates/utils.ts
@@ -1,6 +1,8 @@
+import { profileTags } from 'oa-shared'
+
import { valuesAreDeepEqual } from '../Utils'
-import type { IUserDB } from 'oa-shared/models/user'
+import type { ISelectedTags, ITag, IUserDB } from 'oa-shared'
export const hasDetailsChanged = (
prevUser: IUserDB,
@@ -46,6 +48,7 @@ export const hasDetailsForMapPinChanged = (
prevUser.isContactableByPublic !== user.isContactableByPublic,
prevUser.profileType !== user.profileType,
prevUser.workspaceType !== user.workspaceType,
+ hasUserTagsChanged(prevUser, user),
...hasDetailsChanged(prevUser, user),
...hasLocationDetailsChanged(prevUser, user),
]
@@ -66,6 +69,13 @@ export const hasUserImageChanged = (
if (!prevUser.userImage && user.userImage) return true
}
+export const hasUserTagsChanged = (
+ prevUser: IUserDB,
+ user: IUserDB,
+): boolean => {
+ return !valuesAreDeepEqual(prevUser.tags, user.tags)
+}
+
export const getCreatorImage = (userImage: IUserDB['userImage']) => {
return userImage?.downloadUrl || null
}
@@ -73,3 +83,14 @@ export const getCreatorImage = (userImage: IUserDB['userImage']) => {
export const getFirstCoverImage = (coverImages: IUserDB['coverImages']) => {
return coverImages?.[0]?.downloadUrl || null
}
+
+// For ease, duplicated from src/utils/getValidTags.ts
+export const getValidTags = (tagIds: ISelectedTags) => {
+ const selectedTagIds = Object.keys(tagIds).filter((id) => tagIds[id] === true)
+ const tags: ITag[] = selectedTagIds
+ .map((id) => profileTags.find(({ _id }) => id === _id))
+ .filter((tag): tag is ITag => !!tag)
+ .filter(({ _deleted }) => _deleted !== true)
+
+ return tags
+}
diff --git a/package.json b/package.json
index f93855db08..6c7041f04e 100644
--- a/package.json
+++ b/package.json
@@ -14,8 +14,8 @@
"main": "lib/index.js",
"type": "module",
"scripts": {
- "start": "concurrently --kill-others --names themes,components,platform --prefix-colors cyan,blue,magenta \"yarn start:shared\" \"yarn start:themes\" \"yarn start:components\" \"yarn start:platform\"",
- "start-ci": "concurrently --kill-others --names themes,components,platform --prefix-colors cyan,blue,magenta \"yarn start:themes\" \"yarn start:components\" \"yarn start:platform-ci\"",
+ "start": "concurrently --kill-others --names shared,themes,components,platform --prefix-colors cyan,blue,magenta \"yarn start:shared\" \"yarn start:themes\" \"yarn start:components\" \"yarn start:platform\"",
+ "start-ci": "concurrently --kill-others --names shared,themes,components,platform --prefix-colors cyan,blue,magenta \"yarn start:themes\" \"yarn start:components\" \"yarn start:platform-ci\"",
"start:themes": "yarn workspace oa-themes dev",
"start:components": "yarn workspace oa-components dev",
"start:shared": "yarn workspace oa-shared dev",
@@ -38,7 +38,7 @@
"format:style": "prettier --write '**/*.{json,js,tsx,ts}'",
"serve": "npx serve -s build",
"test": "yarn workspace oa-cypress start",
- "test:components": "yarn workspace oa-components test",
+ "test:components": "yarn build:themes && yarn build:components && yarn workspace oa-components test",
"test:unit": "yarn build:themes && yarn build:components && vitest",
"test:madge": "npx madge --circular --extensions ts,tsx ./ --exclude src/stores",
"storybook": "yarn workspace oa-components start",
@@ -71,9 +71,9 @@
"@emotion/react": "^11.11.4",
"@emotion/server": "^11.11.0",
"@emotion/styled": "^11.11.5",
- "@remix-run/express": "^2.11.1",
- "@remix-run/node": "^2.11.1",
- "@remix-run/react": "^2.11.1",
+ "@remix-run/express": "^2.12.1",
+ "@remix-run/node": "^2.12.1",
+ "@remix-run/react": "^2.12.1",
"@sentry/react": "7.56.0",
"@sentry/remix": "^8.26.0",
"@uppy/compressor": "^1.1.4",
@@ -99,6 +99,7 @@
"fuse.js": "^6.4.6",
"helmet": "^7.1.0",
"isbot": "^5.1.13",
+ "keyv": "^5.1.2",
"leaflet": "^1.5.1",
"leaflet.markercluster": "^1.4.1",
"mobx": "6.9.0",
@@ -109,7 +110,7 @@
"react": "18.3.1",
"react-country-flag": "^3.1.0",
"react-dom": "18.3.1",
- "react-dropzone": "^14.2.3",
+ "react-dropzone-esm": "^15.0.1",
"react-final-form": "6.5.3",
"react-final-form-arrays": "^3.1.3",
"react-foco": "^1.3.1",
@@ -129,8 +130,8 @@
"@emotion/babel-plugin": "^11.11.0",
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
"@faker-js/faker": "^8.4.1",
- "@remix-run/dev": "^2.10.3",
- "@remix-run/testing": "^2.11.0",
+ "@remix-run/dev": "^2.12.1",
+ "@remix-run/testing": "^2.12.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@testing-library/jest-dom": "^6.4.6",
diff --git a/packages/components/.storybook/preview.tsx b/packages/components/.storybook/preview.tsx
index e079bac9db..5160cc72d0 100644
--- a/packages/components/.storybook/preview.tsx
+++ b/packages/components/.storybook/preview.tsx
@@ -1,4 +1,4 @@
-import { ThemeProvider } from '@emotion/react'
+import { ThemeProvider } from '@theme-ui/core'
import type { Preview } from '@storybook/react'
import React from 'react'
diff --git a/packages/components/assets/images/plastic-types/hdpe.svg b/packages/components/assets/images/plastic-types/hdpe.svg
deleted file mode 100755
index 4d5a5fefb6..0000000000
--- a/packages/components/assets/images/plastic-types/hdpe.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/ldpe.svg b/packages/components/assets/images/plastic-types/ldpe.svg
deleted file mode 100755
index 2538181824..0000000000
--- a/packages/components/assets/images/plastic-types/ldpe.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/other.svg b/packages/components/assets/images/plastic-types/other.svg
deleted file mode 100755
index a62c4cd4f7..0000000000
--- a/packages/components/assets/images/plastic-types/other.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/pet.svg b/packages/components/assets/images/plastic-types/pet.svg
deleted file mode 100755
index f99758e7c9..0000000000
--- a/packages/components/assets/images/plastic-types/pet.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/pp.svg b/packages/components/assets/images/plastic-types/pp.svg
deleted file mode 100755
index 46dd3aaa01..0000000000
--- a/packages/components/assets/images/plastic-types/pp.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/ps.svg b/packages/components/assets/images/plastic-types/ps.svg
deleted file mode 100755
index 64fad655ab..0000000000
--- a/packages/components/assets/images/plastic-types/ps.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/assets/images/plastic-types/pvc.svg b/packages/components/assets/images/plastic-types/pvc.svg
deleted file mode 100755
index a8e2c74701..0000000000
--- a/packages/components/assets/images/plastic-types/pvc.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/packages/components/package.json b/packages/components/package.json
index ef97f8565c..f002f6d46b 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -22,10 +22,9 @@
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@faker-js/faker": "^7.6.0",
- "@mui/base": "^5.0.0-beta.18",
- "@react-icons/all-files": "^4.1.0",
- "@remix-run/node": "^2.11.1",
- "@remix-run/react": "^2.11.1",
+ "@mui/base": "next",
+ "@remix-run/node": "^2.12.1",
+ "@remix-run/react": "^2.12.1",
"chromatic": "^11.4.0",
"date-fns": "^2.29.3",
"linkify-plugin-mention": "^4.0.2",
@@ -34,15 +33,15 @@
"oa-themes": "workspace:^",
"photoswipe": "^5.4.1",
"react-country-flag": "^3.1.0",
- "react-icons": "^4.3.1",
+ "react-icons": "^5.3.0",
"react-image-crop": "^11.0.5",
"react-player": "^2.16.0",
"react-portal": "^4.2.2",
"react-responsive-masonry": "2.1.7",
"react-router": "6.24.1",
"react-router-dom": "^6.26.0",
- "react-select": "^5.4.0",
- "react-tooltip": "^4.2.21",
+ "react-select": "^5.8.1",
+ "react-tooltip": "^5.28.0",
"storybook": "^7.6.0",
"styled-system": "^5.1.5",
"theme-ui": "^0.16.2",
@@ -55,8 +54,8 @@
},
"devDependencies": {
"@babel/core": "^7.14.3",
- "@remix-run/dev": "^2.11.1",
- "@remix-run/testing": "^2.11.1",
+ "@remix-run/dev": "^2.12.1",
+ "@remix-run/testing": "^2.12.1",
"@storybook/addon-actions": "^7.4.1",
"@storybook/addon-essentials": "^7.4.1",
"@storybook/addon-links": "^7.4.1",
diff --git a/packages/components/scripts/templates/{componentName}.test.tsx.mst b/packages/components/scripts/templates/{componentName}.test.tsx.mst
index d78b9239f6..c029ba57ea 100644
--- a/packages/components/scripts/templates/{componentName}.test.tsx.mst
+++ b/packages/components/scripts/templates/{componentName}.test.tsx.mst
@@ -1,3 +1,7 @@
+import '@testing-library/jest-dom/vitest'
+
+import { describe, expect, it } from 'vitest'
+
import { render } from '../test/utils'
import { Default } from './{{ComponentName}}.stories'
import type { IProps } from './{{ComponentName}}'
diff --git a/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx b/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
index c61371d18b..1076db9ce7 100644
--- a/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
+++ b/packages/components/src/ArticleCallToAction/ArticleCallToAction.tsx
@@ -1,5 +1,4 @@
-import { useTheme } from '@emotion/react'
-import { Flex, Heading, Text } from 'theme-ui'
+import { Flex, Heading, Text, useThemeUI } from 'theme-ui'
import { Username } from '../Username/Username'
@@ -13,7 +12,7 @@ export interface IProps {
export const ArticleCallToAction = (props: IProps) => {
const { author, children, contributors } = props
- const theme = useTheme() as any
+ const { theme } = useThemeUI() as any
return (
{
countryCode,
coverImage,
profileType,
+ tags,
workspaceType,
} = creator
@@ -82,7 +84,8 @@ export const CardDetailsSpaceProfile = ({ creator, isLink }: IProps) => {
isLink={isLink}
/>
- {workspaceType && (
+
+ {workspaceType && profileType === 'workspace' && (
{
}}
/>
)}
+
+ {tags && }
+
{about && (
{aboutTextStart || about}
diff --git a/packages/components/src/Category/Category.tsx b/packages/components/src/Category/Category.tsx
index 68b8008331..7c1e31061d 100644
--- a/packages/components/src/Category/Category.tsx
+++ b/packages/components/src/Category/Category.tsx
@@ -14,6 +14,7 @@ export const Category = (props: Props) => {
return (
{
}
return (
-