WallpaperMachine
당신이 사랑하는 배경화면, 맥에서 새롭게 만나보세요
A free, open-source map of fruit trees and edible plants growing on public land. Expo + InstantDB + Google Maps. AGPLv3.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗A free, open-source map of fruit trees and edible plants growing on public land.
Drop a pin. Share what's ripe. Eat from your own neighborhood.
Web app · Website · Docs · Roadmap · Ethics
A community-run map of fruit trees, berries, nuts, greens, and other edible plants growing on public land — street trees, parks, abandoned lots, trailsides, shared fencelines.
Every pin has photos, species info, and a ripeness ring that fills as the season progresses and other people confirm. No ads. No analytics. No selling your location. Ever.
Billions of pounds of fruit fall to sidewalks every year while people buy the same fruit shipped in from other continents. This app tries — modestly — to change that by making the food that's already growing around us visible.
| 💚 Free forever | No ads, no subscriptions, no paywalls. |
| 🔒 Your data stays yours | Fuzzy locations by default. Anonymous reports allowed. No trackers. |
| 🌱 Open source | AGPLv3. Read it, fork it, break it. |
| 🤝 Volunteer-run | Built by foragers, for foragers. |
Open forage.techempower.org/app/ in any browser. No install, no account required to browse the map.
Download the latest preview APK from GitHub Releases, enable "Install from unknown sources," and tap to install. The build uses the live InstantDB backend, so pins you drop are real. iOS store build is queued.
15 minutes from clone to running on a simulator.
Prerequisites:
.nvmrc is included)git clone https://github.com/techempower-org/forageforall.git
cd forageforall
nvm use
npm install
cp .env.example .env # fill in INSTANT_APP_ID + Google Maps keys
npm run schema:push # create DB entities in your InstantDB app
npm run seed:species # load ~85 edibles (worldwide + Sierra Nevada natives)
npm run seed:listings # (optional) aggregate open-data pins from iNaturalist + OSM + GBIF
npx expo start # scan QR with Expo Go to run on your phone
For native builds (required to test the Google Maps integration):
npx expo prebuild --clean # generates ios/ and android/ directories
npm run ios # or: npm run android
Full walkthrough: docs/SETUP.md
| Layer | Tech | Why |
|---|---|---|
| Framework | Expo SDK 51 (React Native 0.74) | Cross-platform, OTA updates, managed build pipeline |
| Routing | Expo Router v3 | File-based routes — a file in app/ is a screen |
| Backend | InstantDB | Realtime sync + auth + permissions, no custom server |
| Maps | react-native-maps + Google Maps SDK | Custom paper-textured style |
| Web maps | @react-google-maps/api | Aliased for web via metro.config.js — same API keys |
| Language | TypeScript (strict) | Required throughout — no any escapes |
No custom backend. InstantDB handles auth, realtime sync, and access control. This keeps the app deployable by a single volunteer.
Full architecture: docs/ARCHITECTURE.md
Community pins are the heart, but we also seed the map from open data so you see something the first time you open the app. Every listing carries a source tag, and each source is rendered as its own map layer with a distinct pin color. Toggle them on/off inside the app via the layers icon on the map.
| Source | Key | License | Coverage |
|---|---|---|---|
| Forage for All community | community | AGPLv3 (code), CC BY-SA 4.0 (data) | User-submitted, fuzzy to ~110m |
| iNaturalist | inat | CC BY-NC 4.0 | Research-grade observations globally |
| GBIF | gbif | CC0 / CC BY 4.0 | Scientific occurrences |
| OpenStreetMap | osm | ODbL | Tagged fruit/nut trees in urban areas |
| Falling Fruit | fallingfruit | CC BY-SA 4.0 | Public-land foraging map (API key required) |
| SF Street Trees | sf_trees | PDDL | San Francisco edible species |
| NYC Street Tree Census | nyc_trees | CC0 | NYC edible genera |
| Portland Trees | portland_trees | PDDL | Portland edible species |
Refresh the map with npm run seed:listings (pulls all sources for every region) or target one: npm run seed:listings -- --source inat --region "Nevada County, CA".
First run of the aggregator seeded 3,603 edible-plant pins for Nevada County, CA alone — manzanita, toyon, black oak, elderberry, wild grape, plus the expected fruit trees — pulled from iNaturalist and GBIF. Each pin is linked to a species entity in the catalog, so photos, season, toxicity, and look-alike warnings light up on tap. A weekly GitHub Action (.github/workflows/sync-data.yml) re-runs the aggregation so the map stays fresh, and both seeds are idempotent (deterministic UUID-v5 IDs keyed off latin name + sourceId) so re-runs upsert in place.
forageforall/
├── app/ # Expo Router screens (file = route)
│ ├── (tabs)/ # Map, Browse, In Season, Profile tabs
│ │ ├── index.web.tsx # Web map screen (@react-google-maps/api)
│ ├── listing/[id].tsx # Pin detail sheet
│ ├── add.tsx # Add-pin flow
│ ├── auth.tsx # Magic-link sign in
│ └── onboarding.tsx
├── src/
│ ├── components/ # Pin, RipenessRing, SeasonStrip, Chip, LayerSheet
│ ├── config/
│ │ ├── mapStyles.ts # Paper / Dark / Satellite custom Google Maps styles
│ │ └── sourceLayers.ts # Registry of open-data sources + attribution
│ ├── db/
│ │ ├── client.web.ts # Web InstantDB client (@instantdb/react, Metro .web.ts resolution)
│ │ └── schema.ts # InstantDB schema (source of truth)
│ ├── hooks/
│ │ ├── useListings.ts # Viewport-aware geohash query with layer filtering
│ │ ├── useSourceLayers.ts # Persisted (AsyncStorage) layer toggle state
│ │ └── useCurrentLocation.ts
│ ├── lib/
│ │ ├── geo.ts # Geohash index + fuzzy location (read before touching)
│ │ ├── ripeness.ts # Time-weighted ripeness math (14-day half-life)
│ │ └── season.ts # Month-window helpers
│ └── theme/
│ └── tokens.ts # Colors, type scale, spacing — single source of truth
├── assets/ # Icon, splash, adaptive icon
├── docs/ # GitHub Pages site (static HTML, no build step)
├── scripts/
│ ├── species-data.ts # Shared species catalog (single source of truth for both seeds)
│ ├── seed-species.ts # Idempotent upsert of ~85 species (SHA-1 UUIDs keyed off latin name)
│ ├── seed-listings.ts # Aggregates listings from iNat + GBIF + OSM + city datasets,
│ │ # links each to its species, denormalises kind + ripeness
│ └── sync-listings.ts # Refreshes stale open-data listings per-source
├── instant.schema.ts # Re-exports src/db/schema for the Instant CLI
├── instant.perms.ts # Row-level permissions for InstantDB
├── app.config.ts # Expo config — reads env vars for keys + IDs
├── eas.json # EAS build profiles (preview + production)
└── metro.config.js # Aliases @instantdb/react-native → @instantdb/react on web
Entities in src/db/schema.ts (push changes with npm run schema:push):
| Entity | Purpose |
|---|---|
species | Catalog — common name, Latin name, kind, seasonMonths, toxicity, look-alikes |
listings | Pins (community + imports) — fuzzed location, geohash5/7, kind (denormalised), linked species, currentRipeness, source, sourceId, sourceSyncedAt |
reports | Ripeness/presence confirmations on a listing |
comments | Text notes on a listing |
profiles | User profiles — handle, badge count, privacy prefs |
saves | Bookmarked listings |
flags | Moderation flags on a listing |
Listings include source (community · inat · gbif · osm · fallingfruit · sf_trees · nyc_trees · portland_trees) and sourceId (e.g. inat:12345) so imports can be idempotently upserted. The kind field is indexed and filterable — lets the map filter by category without forcing a join.
We especially need:
Before opening a PR, read:
CONTRIBUTING.md — setup, code style, PR flowAGENTS.md — conventions for AI coding agents (good reading for humans too)FORAGING_ETHICS.md — the community codeCODE_OF_CONDUCT.mdFirst-timers: look for issues tagged good first issue.
Take a third, leave a third for the birds, leave a third for the earth.
Full doc: FORAGING_ETHICS.md
Some things get asked for often. The answer is no, and it's not changing:
GNU AGPLv3 — you can use, modify, and redistribute this code, but:
This is deliberate. Community-contributed foraging data shouldn't end up behind a paywall.
src/lib/maps.ts (note: file forthcoming)"Eating is an agricultural act." — Wendell Berry
Made with 🫐 by volunteers. Join us.
Forage for All is a project of TechEMPOWER, a 501(c)(3) nonprofit in Grass Valley, California: free technology help, a benefits screener and guides for low-income families.