Compare commits

..

49 Commits

Author SHA1 Message Date
Daniel O'Connor
9b1699b061 Merge pull request #4215 from Growstuff/activities-detail
Surface more Activities detail
2025-09-07 14:39:13 +09:30
Daniel O'Connor
06c907742d Merge pull request #4216 from Growstuff/CloCkWeRX-patch-2
Update ci-features-posts.yml
2025-09-07 14:38:54 +09:30
Daniel O'Connor
d8b84e611b Update ci-features-posts.yml 2025-09-07 14:37:18 +09:30
Daniel O'Connor
c0ab0b085e Merge pull request #4214 from Growstuff/fix-cards
Fix styling of cards to space evenly
2025-09-07 14:34:14 +09:30
Daniel O'Connor
99d50a7d4b Merge pull request #4212 from Growstuff/fix-ordering
Change garden sort order to be by name and planting by most to least recent when creating an activity
2025-09-07 14:33:46 +09:30
Daniel O'Connor
84da4c0f4f Fix styling of cards to space evenly 2025-09-07 04:24:22 +00:00
Daniel O'Connor
f650d1b8fa Change garden sort order to be by name and planting by most to lease recent when creating an activity 2025-09-07 03:54:20 +00:00
dependabot[bot]
91ef85da0b Merge pull request #4203 from Growstuff/dependabot/bundler/rubocop-1.80.2 2025-09-05 19:09:32 +00:00
dependabot[bot]
30f799c4b9 Bump rubocop from 1.80.1 to 1.80.2
Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.80.1 to 1.80.2.
- [Release notes](https://github.com/rubocop/rubocop/releases)
- [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop/compare/v1.80.1...v1.80.2)

---
updated-dependencies:
- dependency-name: rubocop
  dependency-version: 1.80.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 08:10:40 +00:00
dependabot[bot]
9283e64366 Merge pull request #4204 from Growstuff/dependabot/bundler/puma-7.0.0 2025-09-05 08:09:30 +00:00
dependabot[bot]
b0b759ef60 Bump puma from 6.6.1 to 7.0.0
Bumps [puma](https://github.com/puma/puma) from 6.6.1 to 7.0.0.
- [Release notes](https://github.com/puma/puma/releases)
- [Changelog](https://github.com/puma/puma/blob/master/History.md)
- [Commits](https://github.com/puma/puma/compare/v6.6.1...v7.0.0)

---
updated-dependencies:
- dependency-name: puma
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-05 07:59:48 +00:00
Daniel O'Connor
83cf4117fb Merge pull request #4201 from Growstuff/feature/preserve-screenshots-as-artifacts
CI: Preserve screenshots as build artifacts in all feature workflows
2025-09-05 17:29:15 +09:30
Daniel O'Connor
521e649cac Merge pull request #4202 from Growstuff/dependabot/bundler/pg-1.6.2
Bump pg from 1.6.1 to 1.6.2
2025-09-04 20:46:29 +09:30
dependabot[bot]
1dc587d4b5 Bump pg from 1.6.1 to 1.6.2
Bumps [pg](https://github.com/ged/ruby-pg) from 1.6.1 to 1.6.2.
- [Changelog](https://github.com/ged/ruby-pg/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ged/ruby-pg/compare/v1.6.1...v1.6.2)

---
updated-dependencies:
- dependency-name: pg
  dependency-version: 1.6.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-04 00:26:26 +00:00
Daniel O'Connor
77a6e96ece Merge branch 'dev' into feature/preserve-screenshots-as-artifacts 2025-09-04 08:43:44 +09:30
dependabot[bot]
2e89dc0c4f Merge pull request #4200 from Growstuff/dependabot/bundler/rubocop-rspec-3.7.0 2025-09-03 09:49:39 +00:00
dependabot[bot]
dc1b46c7be Bump rubocop-rspec from 3.6.0 to 3.7.0
Bumps [rubocop-rspec](https://github.com/rubocop/rubocop-rspec) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/rubocop/rubocop-rspec/releases)
- [Changelog](https://github.com/rubocop/rubocop-rspec/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop-rspec/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: rubocop-rspec
  dependency-version: 3.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-03 05:55:09 +00:00
google-labs-jules[bot]
d61227bad0 CI: Preserve screenshots as build artifacts
Adjust the behaviour of capybara-screenshot / GitHub CI to preserve the screenshots as a build artifact.

This change adds a step to all `ci-features-*.yml` workflows to upload the
`tmp/screenshots` directory as a build artifact on failure. This will help with
debugging failing feature tests.
2025-09-02 23:30:25 +00:00
google-labs-jules[bot]
765fab1104 CI: Preserve screenshots as build artifacts
Adjust the behaviour of capybara-screenshot / GitHub CI to preserve the screenshots as a build artifact.

This change adds a step to the `ci-features.yml` workflow to upload the
`tmp/screenshots` directory as a build artifact on failure. This will help with
debugging failing feature tests.
2025-09-02 23:30:02 +00:00
Daniel O'Connor
8000a51e8b Remove JS testing from footer (#4192) 2025-09-02 07:42:09 +09:30
Daniel O'Connor
b3ba05d834 Fix crash on adding Flickr photo (#4198)
* Update photo.rb

* Update photo.rb

* Update app/models/photo.rb

* Update app/models/photo.rb
2025-09-02 02:17:28 +09:30
Daniel O'Connor
110b18cc9e Merge pull request #4197 from Growstuff/CloCkWeRX-patch-4
Update README.md
2025-09-02 00:37:09 +09:30
Daniel O'Connor
396af468fa Merge branch 'dev' into CloCkWeRX-patch-4 2025-09-02 00:36:56 +09:30
Daniel O'Connor
ac2d998711 Update README.md 2025-09-02 00:36:30 +09:30
Daniel O'Connor
4564d0afe0 Merge pull request #4196 from Growstuff/CloCkWeRX-patch-3
Update README.md
2025-09-02 00:34:07 +09:30
Daniel O'Connor
a325ada964 Merge pull request #4194 from Growstuff/CloCkWeRX-patch-2
Rename _facts.haml to _facts.html.haml
2025-09-02 00:33:35 +09:30
Daniel O'Connor
2b818e9f50 Update README.md 2025-09-02 00:33:12 +09:30
Daniel O'Connor
9b9de06140 Update README.md 2025-09-02 00:12:59 +09:30
Daniel O'Connor
2f290efc5b Rename _facts.haml to _facts.html.haml 2025-09-02 00:03:04 +09:30
Daniel O'Connor
23ef0f9cac Merge pull request #4193 from Growstuff/CloCkWeRX-patch-2
Update _facts.haml
2025-09-02 00:02:31 +09:30
Daniel O'Connor
7106b141d9 Update _facts.haml 2025-09-02 00:02:17 +09:30
Daniel O'Connor
ada567dcab Remove JS testing from footer 2025-09-01 14:28:24 +00:00
Daniel O'Connor
d620dc3bfc Merge pull request #4190 from Growstuff/less-js
Specs: Sign up, sign in don't need JS
2025-09-01 23:57:29 +09:30
Daniel O'Connor
c189e3b01a Merge pull request #4062 from Growstuff/feature/planting-rating
Add overall_rating to Plantings
2025-09-01 23:56:13 +09:30
Daniel O'Connor
70e6c44d82 Sign up, sign in don't need JS 2025-09-01 13:20:04 +00:00
Daniel O'Connor
b69d1bd14b Merge pull request #4189 from Growstuff/remove-openfarm-service
Remove openfarm service
2025-09-01 22:34:52 +09:30
Daniel O'Connor
468e34a551 Remove openfarm service 2025-09-01 12:56:22 +00:00
Daniel O'Connor
8385beb406 Merge pull request #4188 from Growstuff/remove-dead-gems
Remove haml-lint-extractor
2025-09-01 21:55:26 +09:30
google-labs-jules[bot]
0f4803392d Add seed source to Seed model (#4186)
* Add seed source to Seed model

* Update _form.html.haml

* Add to schema

* Default option

* Default option

* Fix test

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Daniel O'Connor <daniel.oconnor@gmail.com>
2025-09-01 21:47:31 +09:30
Daniel O'Connor
d185ce495f Remove haml-lint-extractor 2025-09-01 12:13:08 +00:00
Daniel O'Connor
90bd70603b Add a lot of indexes (#4187) 2025-09-01 21:06:58 +09:30
Daniel O'Connor
a4db05c0f6 Add a lot of indexes 2025-09-01 11:25:02 +00:00
google-labs-jules[bot]
0079513b35 Merge pull request #4183 from Growstuff/feature/timeline-likes
Feature: Display likes on timeline
2025-09-01 19:51:24 +09:30
Daniel O'Connor
508ee5260d Merge pull request #4180 from Growstuff/fix/profile-bio-link
Fix: Only show 'add a bio' link on own profile
2025-09-01 15:39:52 +09:30
google-labs-jules[bot]
ee2fffd25b Fix: Only show 'add a bio' link on own profile
The 'add a bio' link on the member profile page was previously shown
based on the `can? :edit, @member` ability check. This caused an issue
for admins, who could see the link on other users' profiles, but the
link would incorrectly lead to their own settings page.

This change modifies the condition to be `member_signed_in? && current_member == @member`.
This ensures the link is only displayed when a logged-in user is
viewing their own profile, which is the correct and intended behavior.
2025-08-31 22:48:41 +00:00
Daniel O'Connor
c92b912b28 Fix link 2025-08-31 15:46:18 +09:30
dependabot[bot]
f665fba91a Merge pull request #4077 from Growstuff/dependabot/bundler/terser-1.2.6 2025-08-31 06:02:22 +00:00
Daniel O'Connor
3578fbdf64 Merge branch 'dev' into dependabot/bundler/terser-1.2.6 2025-08-31 15:24:25 +09:30
dependabot[bot]
6d44a2a780 Bump terser from 1.2.5 to 1.2.6
Bumps [terser](https://github.com/ahorek/terser-ruby) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/ahorek/terser-ruby/releases)
- [Changelog](https://github.com/ahorek/terser-ruby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/ahorek/terser-ruby/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: terser
  dependency-version: 1.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-10 04:49:25 +00:00
54 changed files with 355 additions and 162 deletions

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (admin/)
run: bundle exec rspec spec/features/admin/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (comments/)
run: bundle exec rspec spec/features/comments/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -101,3 +101,9 @@ jobs:
- name: Run rspec (conversations/)
run: bundle exec rspec spec/features/conversations/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (crops/)
run: bundle exec rspec spec/features/crops/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (gardens/)
run: bundle exec rspec spec/features/gardens/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -99,4 +99,11 @@ jobs:
run: bundle exec rails search:reindex
- name: Run rspec (harvests/)
run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky
run: bundle exec rspec spec/features/harvests/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -99,4 +99,11 @@ jobs:
run: bundle exec rails search:reindex
- name: Run rspec (home/)
run: bundle exec rspec spec/features/home/ -fd -t ~@flaky
run: bundle exec rspec spec/features/home/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -99,4 +99,11 @@ jobs:
run: bundle exec rails search:reindex
- name: Run rspec (members/)
run: bundle exec rspec spec/features/members/ -fd -t ~@flaky
run: bundle exec rspec spec/features/members/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (places/)
run: bundle exec rspec spec/features/places/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (plantings/)
run: bundle exec rspec spec/features/plantings/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -1,4 +1,4 @@
name: CI Features - Admin
name: CI Features - Posts
on: [pull_request]
@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (posts/)
run: bundle exec rspec spec/features/posts/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -100,3 +100,10 @@ jobs:
- name: Run rspec (seeds/)
run: bundle exec rspec spec/features/seeds/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -99,4 +99,11 @@ jobs:
run: bundle exec rails search:reindex
- name: Run rspec (timeline/)
run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky
run: bundle exec rspec spec/features/timeline/ -fd -t ~@flaky
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -108,4 +108,11 @@ jobs:
run: bundle exec rspec spec/features/photos/ -fd
- name: Run rspec (rss/)
run: bundle exec rspec spec/features/rss/ -fd
run: bundle exec rspec spec/features/rss/ -fd
- name: Upload screenshots
if: failure()
uses: actions/upload-artifact@v4
with:
name: screenshots
path: tmp/screenshots

View File

@@ -178,7 +178,6 @@ group :development, :test do
gem 'dotenv-rails'
# cli utils
gem 'haml-i18n-extractor', require: false
gem 'haml_lint', '>= 0.25.1', require: false # Checks haml files for goodness
gem 'i18n-tasks', require: false # adds tests for finding missing and unused translations
gem 'rspectre', require: false # finds unused code in specs

View File

@@ -294,12 +294,6 @@ GEM
temple (>= 0.8.2)
thor
tilt
haml-i18n-extractor (0.5.9)
activesupport
haml
highline
tilt
trollop (= 1.16.2)
haml-rails (2.1.0)
actionpack (>= 5.1)
activesupport (>= 5.1)
@@ -463,8 +457,8 @@ GEM
racc
percy-capybara (5.0.0)
capybara (>= 3)
pg (1.6.1)
pg (1.6.1-x86_64-linux)
pg (1.6.2)
pg (1.6.2-x86_64-linux)
platform-api (3.8.0)
heroics (~> 0.1.1)
moneta (~> 1.0.0)
@@ -481,7 +475,7 @@ GEM
date
stringio
public_suffix (6.0.1)
puma (6.6.1)
puma (7.0.0)
nio4r (~> 2.0)
query_diet (0.7.2)
racc (1.8.1)
@@ -607,7 +601,7 @@ GEM
rswag-ui (2.16.0)
actionpack (>= 5.2, < 8.1)
railties (>= 5.2, < 8.1)
rubocop (1.80.1)
rubocop (1.80.2)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
@@ -636,7 +630,7 @@ GEM
rubocop-rake (0.7.1)
lint_roller (~> 1.1)
rubocop (>= 1.72.1)
rubocop-rspec (3.6.0)
rubocop-rspec (3.7.0)
lint_roller (~> 1.1)
rubocop (~> 1.72, >= 1.72.1)
rubocop-rspec_rails (2.31.0)
@@ -694,14 +688,13 @@ GEM
temple (0.10.4)
terminal-table (4.0.0)
unicode-display_width (>= 1.1.1, < 4)
terser (1.2.5)
terser (1.2.6)
execjs (>= 0.3.0, < 3)
thor (1.4.0)
thread_safe (0.3.6)
tilt (2.6.1)
timecop (0.9.10)
timeout (0.4.3)
trollop (1.16.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.5)
@@ -788,7 +781,6 @@ DEPENDENCIES
gibbon (~> 1.2.0)
gravatar-ultimate
haml
haml-i18n-extractor
haml-rails
haml_lint (>= 0.25.1)
hashie (>= 3.5.3)

View File

@@ -17,7 +17,9 @@ encourage participation from people of all backgrounds and skill levels.
## Want to contribute?
Don't ask to ask, the best way to get started is to fork the project, start a codespace and get hacking.
Dive on in and submit your PRs.
Dive on in and submit your PRs!
Vibe Coding is more than okay, just make sure you indicate if you have done so and ensure there are tests.
## Important links
@@ -35,6 +37,10 @@ frontend features. We welcome contributions -- see
* To set up your development environment, see [Getting started](https://github.com/Growstuff/growstuff/wiki/New-contributor-guide).
* You may also be interested in our [API](https://github.com/Growstuff/growstuff/wiki/API).
### For Home Automation enthusiasts
https://github.com/Growstuff/homeassistant-growstuff/
## For designers, writers, researchers, data wranglers, and other contributors
There are heaps of ways to get involved and contribute no matter what

View File

@@ -132,6 +132,8 @@ section {
border-radius: 5%;
margin: 0.5em 0.5em 0.5em 0;
width: 200px;
align-items: stretch;
justify-content: space-between;
.img-card {
border-top-left-radius: 5%;

View File

@@ -133,7 +133,7 @@ class PlantingsController < DataController
:crop_id, :description, :garden_id, :planted_at,
:parent_seed_id,
:quantity, :sunniness, :planted_from, :finished,
:finished_at, :failed
:finished_at, :failed, :overall_rating
)
end

View File

@@ -43,6 +43,7 @@ class SeedsController < DataController
def new
@seed = Seed.new
@seed.source = 'my own seed saving'
if params[:planting_slug]
@planting = Planting.find_by(slug: params[:planting_slug])
@@ -56,6 +57,7 @@ class SeedsController < DataController
def create
@seed = Seed.new(seed_params)
@seed.source ||= 'my own seed saving'
@seed.finished ||= false
@seed.owner = current_member
@seed.crop = @seed.parent_planting.crop if @seed.parent_planting
@@ -84,7 +86,7 @@ class SeedsController < DataController
:crop_id, :description, :quantity, :plant_before,
:parent_planting_id, :saved_at,
:days_until_maturity_min, :days_until_maturity_max,
:organic, :gmo,
:organic, :gmo, :source,
:heirloom, :tradable_to, :slug,
:finished, :finished_at
)

View File

@@ -21,6 +21,28 @@ module ApplicationHelper
classes
end
# Similar to Rails' time_ago_in_words, but gives a more standard
# output like "in 3 days" or "5 months ago".
# Also handles the case where from_time is a Date and to_time is a Date
# (in which case it just says "today" if they're the same date).
#
# NOTE: This is similar to distance_of_time_in_words but different enough
# that I think it's worth having a separate helper for it.
#
# from_time - the starting time (Time or Date)
# to_time - the ending time (Time or Date). Default: now (Time.zone.now)
# include_seconds - whether to include seconds in the calculation
#
# Returns a string like "in 3 days" or "5 months ago"
def standard_time_distance(from_time, to_time = 0, include_seconds = false)
return 'today' if from_time.is_a?(Date) && (from_time == to_time)
return 'now' if from_time == to_time
return distance_of_time_in_words(from_time, to_time, include_seconds:) + ' ago' if from_time > to_time
'in ' + distance_of_time_in_words(from_time, to_time, include_seconds:)
end
def count_github_contibutors
File.open(Rails.root.join('CONTRIBUTORS.md')).readlines.grep(/^-/).size
end

View File

@@ -7,6 +7,8 @@ module EventHelper
def event_description(event)
render "#{event.event_type.pluralize}/description", event_model: resolve_model(event)
rescue ActionView::MissingTemplate
"#{event.event_type.humanize.downcase}d"
end
def resolve_model(event)

View File

@@ -9,7 +9,9 @@ module SearchActivities
mappings: {
properties: {
active: { type: :boolean },
created_at: { type: :integer }
created_at: { type: :integer },
updated_at: { type: :integer },
due_date: { type: :date }
}
}
@@ -23,8 +25,10 @@ module SearchActivities
category:,
garden_id:,
garden_name: garden&.name,
garden_slug: garden&.garden_slug,
planting_id:,
planting_name: planting&.crop&.name,
planting_slug: planting&.slug,
description:,
# owner

View File

@@ -46,7 +46,8 @@ class Photo < ApplicationRecord
flickr = owner.flickr
info = flickr.photos.getInfo(photo_id: source_id)
licenses = flickr.photos.licenses.getInfo
license = licenses.find { |l| l.id == info.license }
license = licenses.find { |l| l.id.to_i == info.license.to_i }
Rails.logger.error("Cannot find license: " + [info.license, licenses].inspect) unless license
{
title: calculate_title(info),
license_name: license.name,

View File

@@ -83,6 +83,9 @@ class Planting < ApplicationRecord
validates :planted_from, allow_blank: true, inclusion: {
in: PLANTED_FROM_VALUES, message: "%<value>s is not a valid planting method"
}
validates :overall_rating, allow_blank: true, numericality: {
only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5
}
def planting_slug
[

View File

@@ -12,6 +12,8 @@ class Seed < ApplicationRecord
ORGANIC_VALUES = ['certified organic', 'non-certified organic', 'conventional/non-organic', 'unknown'].freeze
GMO_VALUES = ['certified GMO-free', 'non-certified GMO-free', 'GMO', 'unknown'].freeze
HEIRLOOM_VALUES = %w(heirloom hybrid unknown).freeze
SOURCE_VALUES = ['seed catalogue', 'retail outlet', 'seed bank or similar institution',
'traded from another person', 'my own seed saving', 'other/unknown'].freeze
#
# Relationships
@@ -44,6 +46,9 @@ class Seed < ApplicationRecord
validates :heirloom, allow_blank: false,
inclusion: { in: HEIRLOOM_VALUES, message: "You must say whether the seeds" \
"are heirloom, hybrid, or unknown" }
validates :source, allow_blank: true,
inclusion: { in: SOURCE_VALUES, message: "You must say where the seeds are from," \
"or that you don't know" }
#
# Delegations

View File

@@ -1,108 +0,0 @@
# frozen_string_literal: true
BASE = 'https://openfarm.cc/api/v1/'
# BASE = 'http://127.0.0.1:3000/api/v1/'
class OpenfarmService
def initialize
@cropbot = Member.find_by(login_name: 'cropbot')
end
def import!
Crop.all.order(updated_at: :desc).each do |crop|
Rails.logger.debug { "#{crop.id}, #{crop.name}" }
update_crop(crop) if crop.valid?
end
end
def update_crop(crop)
openfarm_record = fetch(crop.name)
if openfarm_record.present? && openfarm_record.is_a?(String)
Rails.logger.info(openfarm_record)
elsif openfarm_record.present? && openfarm_record.fetch('data', false)
crop.update! openfarm_data: openfarm_record.fetch('data', false)
save_companions(crop, openfarm_record)
save_photos(crop)
else
Rails.logger.debug "\tcrop not found on Open Farm"
crop.update!(openfarm_data: false)
end
end
def save_companions(crop, openfarm_record)
companions = openfarm_record.fetch('data').fetch('relationships').fetch('companions').fetch('data')
crops = openfarm_record.fetch('included', []).select { |rec| rec["type"] == 'crops' }
CropCompanion.transaction do
companions.each do |com|
companion_crop_hash = crops.detect { |c| c.fetch('id') == com.fetch('id') }
companion_crop_name = companion_crop_hash.fetch('attributes').fetch('name').downcase
companion_crop = Crop.where('lower(name) = ?', companion_crop_name).first
companion_crop = Crop.create!(name: companion_crop_name, requester: @cropbot, approval_status: "pending") if companion_crop.nil?
crop.companions << companion_crop unless crop.companions.where(id: companion_crop.id).any?
end
end
end
def save_photos(crop)
pictures = fetch_pictures(crop.name)
pictures.each do |picture|
data = picture.fetch('attributes')
Rails.logger.debug(data)
next unless data.fetch('image_url').start_with? 'http'
next if Photo.find_by(source_id: picture.fetch('id'), source: 'openfarm')
photo = Photo.new(
source_id: picture.fetch('id'),
source: 'openfarm',
owner: @cropbot,
thumbnail_url: data.fetch('thumbnail_url'),
fullsize_url: data.fetch('image_url'),
title: 'Open Farm photo',
license_name: 'No rights reserved',
link_url: "https://openfarm.cc/en/crops/#{name_to_slug(crop.name)}"
)
if photo.valid?
Photo.transaction do
photo.save
PhotoAssociation.find_or_create_by! photo:, photographable: crop
end
Rails.logger.debug { "\t saved photo #{photo.id} #{photo.source_id}" }
else
Rails.logger.warn "Photo not valid"
end
end
end
def fetch(name)
conn.get("crops/#{name_to_slug(name)}.json").body
rescue NoMethodError
Rails.logger.debug "error fetching crop"
Rails.logger.debug "BODY: "
Rails.logger.debug body
end
def name_to_slug(name)
CGI.escape(name.gsub(' ', '-').downcase)
end
def fetch_all(page)
conn.get("crops.json?page=#{page}").body.fetch('data', {})
end
def fetch_pictures(name)
body = conn.get("crops/#{name_to_slug(name)}/pictures.json").body
body.fetch('data', false)
rescue StandardError
Rails.logger.debug "Error fetching photos"
Rails.logger.debug []
end
private
def conn
Faraday.new BASE do |conn|
conn.response :json, content_type: /\bjson$/
conn.adapter Faraday.default_adapter
end
end
end

View File

@@ -18,10 +18,20 @@ class TimelineService
.union_all(photos_query)
.union_all(seeds_query)
.union_all(activities_query)
.union_all(likes_query)
.where.not(event_at: nil)
.order(event_at: :desc)
end
def self.likes_query
Like
.select("likes.id",
"'like' as event_type",
"likes.created_at as event_at",
"likes.member_id as owner_id",
"null as crop_id")
end
def self.activities_query
Activity.select(
:id,

View File

@@ -20,16 +20,23 @@
- if can? :destroy, activity
.dropdown-divider
= delete_button(activity, classes: 'dropdown-item text-danger')
= link_to activity_path(slug: activity.slug) do
.card-body.text-center
.card-body
= link_to activity_path(slug: activity.slug) do
%h4= activity.name
.text-center= activity.description
- if activity.garden
.text-center= activity.garden
- if activity.planting
.text-center= activity.planting
%small.due-date{title: activity.due_date}
= standard_time_distance(activity.due_date.to_date, Time.zone.now.to_date)
%div
%small.text-justify{title: activity.description}= activity.description.truncate(150)
%p
%ul.list-unstyled
- if activity.garden_name && activity.garden_slug
%li
%small= link_to activity.garden_name, garden_path(slug: activity.garden_slug)
- if activity.planting_name && activity.planting_slug
%li
%small= link_to activity.planting_name, planting_path(slug: activity.planting_slug)
.card-footer
.float-right
%span.chip.member-chip
= link_to member_path(slug: activity.owner_slug) do
= activity.owner_login_name
%small.chip.member-chip
= link_to member_path(slug: activity.owner_slug) do
= activity.owner_login_name

View File

@@ -27,13 +27,13 @@
.row
.col-md-4
= f.collection_radio_buttons(:garden_id, @activity.owner.gardens.active,
= f.collection_radio_buttons(:garden_id, @activity.owner.gardens.active.order_by_name,
:id, :name,
label: 'Is this for a specific garden?')
= link_to "Add a garden.", new_garden_path
.col-md-4
= f.collection_radio_buttons(:planting_id, @activity.owner.plantings.active,
= f.collection_radio_buttons(:planting_id, @activity.owner.plantings.active.recent,
:id, :crop_name,
label: 'Is this for a specific planting?')
= link_to "Add a planting.", new_planting_path

View File

@@ -12,7 +12,7 @@
#{harvest.owner} harvested #{display_quantity(harvest)}.
.float-right= render 'members/location', member: harvest.owner
.harvest-timeago
%small #{distance_of_time_in_words(harvest.harvested_at, Time.zone.now)} ago.
%small #{standard_time_distance(harvest.harvested_at, Time.zone.now.to_date)}
%li.list-group-item= link_to "View all #{crop.name} harvests", crop_harvests_path(crop), class: 'card-link'
- if crop.approved?
- if current_member

View File

@@ -5,3 +5,8 @@
- @matching_plantings.each do |planting|
= f.radio_button :planting_id, planting.id, label: planting
= f.submit "save", class: 'btn btn-sm'
- if @harvest.planting.present? && @harvest.planting.overall_rating.blank?
.alert.alert-info{role: "alert"}
This harvest is from a planting that hasn't been rated yet.
= link_to "Rate this planting", edit_planting_path(@harvest.planting), class: 'alert-link'

View File

@@ -46,7 +46,7 @@
%h3
Harvested
= editable :date, @harvest, :harvested_at, display_field: '.harvested_at'
%strong.harvested_at #{distance_of_time_in_words @harvest.harvested_at, Time.zone.now.to_date} ago
%strong.harvested_at #{standard_time_distance @harvest.harvested_at, Time.zone.now.to_date}
%span.harvested_at= I18n.l @harvest.harvested_at
.card{class: @harvest.quantity.present? ? '' : 'text-muted'}

View File

@@ -5,5 +5,5 @@
number_crops: link_to(t('.number_crops_linktext', count: Crop.count.to_i), crops_path),
number_plantings: link_to(t('.number_plantings_linktext', count: Planting.count.to_i), plantings_path),
number_gardens: link_to(t('.number_gardens_linktext', count: Garden.count.to_i), gardens_path),
contributors: link_to(count_github_contibutors, 'http://github.com/Growstuff/growstuff/CONTRIBUTORS.md', target: '_blank', rel: 'noopener'),
contributors: link_to(count_github_contibutors, 'https://github.com/Growstuff/growstuff/blob/dev/CONTRIBUTORS.md', target: '_blank', rel: 'noopener'),
github: link_to('GitHub', 'http://github.com/Growstuff/growstuff', target: '_blank', rel: 'noopener'))

View File

@@ -0,0 +1 @@
#{link_to event_model.member, event_model.member} liked #{link_to event_model.likeable.class.name.downcase, event_model.likeable}

View File

@@ -28,7 +28,7 @@
%a{href: "#content"}
Skip to main content
- if @member.bio.blank?
- if can? :edit, @member
- if member_signed_in? && current_member == @member
= link_to "Add a bio to complete your profile.", edit_member_registration_path
- else
#{@member.login_name} hasn't written a bio yet.

View File

@@ -89,3 +89,9 @@
- if planting.finished_at.present?
%span.plantingfact--finish
= planting.finished_at.year
- if planting.overall_rating.present?
.card.fact-card
.card-body
%h3 Overall Rating
%p.card-text
%strong= "#{planting.overall_rating}/5"

View File

@@ -43,6 +43,15 @@
= f.select(:sunniness, Planting::SUNNINESS_VALUES, { include_blank: '', label: 'Sun or shade?' } )
.col-md-4
= f.number_field :quantity, label: 'How many?', min: 1
.col-md-12
= f.range_field :overall_rating, min: 1, max: 5, include_blank: 'Leave blank', label: 'Overall Rating', list: "rating-list", title: "How well is the planting going?"
%datalist{"id": "rating-list"}
%option{"value": "1"} Poor
%option{"value": "2"}
%option{"value": "3"}
%option{"value": "4"}
%option{"value": "5"} Great
= f.text_area :description, rows: 6, label: 'Tell us more about it'
.row

View File

@@ -7,6 +7,15 @@
= tag("meta", property: "og:type", content: "website")
= tag("meta", property: "og:url", content: request.original_url)
= tag("meta", property: "og:site_name", content: ENV['GROWSTUFF_SITE_NAME'])
- if @planting.overall_rating.present?
%script{type: "application/ld+json"}
:plain
{
"@context": "http://schema.org",
"@type": "Rating",
"ratingValue": "#{@planting.overall_rating}",
"bestRating": "5"
}
- content_for :breadcrumbs do
%li.breadcrumb-item= link_to 'Plantings', plantings_path

View File

@@ -49,17 +49,19 @@
.col-md-6= f.number_field :days_until_maturity_max, label_as_placeholder: true, label: 'max', prepend: 'to', append: "days", min: 1
.row
.col-md-4
= f.select(:organic, Seed::ORGANIC_VALUES, {label: 'Organic?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-4
= f.select(:gmo, Seed::GMO_VALUES, {label: 'GMO?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-4
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, {label: 'Heirloom?', wrapper: { class: 'required'}, required: true}, default: 'unknown')
.col-md-3
= f.select(:organic, Seed::ORGANIC_VALUES, { label: 'Organic?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:gmo, Seed::GMO_VALUES, { label: 'GMO?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:heirloom, Seed::HEIRLOOM_VALUES, { label: 'Heirloom?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
.col-md-3
= f.select(:source, Seed::SOURCE_VALUES, { label: 'Source?', wrapper: { class: 'required' }, required: true }, default: 'unknown')
= f.text_area :description, rows: 6
%hr/
= t('.trade_help', site_name: ENV['GROWSTUFF_SITE_NAME'])
= f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, {label: 'Will trade', wrapper: { class: 'required'}, required: true})
= f.select(:tradable_to, Seed::TRADABLE_TO_VALUES, { label: 'Will trade', wrapper: { class: 'required' }, required: true })
%span.help_inline
- if current_member.location.blank?
Don't forget to

View File

@@ -0,0 +1,2 @@
- likeable = like.likeable
= render "timeline/likeables/#{likeable.class.name.downcase}", likeable: likeable

View File

@@ -14,6 +14,7 @@
= link_to owner, owner
= event_description(event)
= render 'timeline/photos', photo: resolve_model(event) if event.event_type == 'photo'
= render 'timeline/like', like: resolve_model(event) if event.event_type == 'like'
%small
- if event.event_at.present?
- if event.event_at.kind_of?(Date)

View File

@@ -0,0 +1 @@
= render 'timeline/photos', photo: likeable

View File

@@ -0,0 +1,6 @@
.card.my-2
.card-body
%blockquote.blockquote.mb-0
%p= truncate(likeable.body, length: 140)
%footer.blockquote-footer
= link_to "view post", likeable

View File

@@ -0,0 +1,6 @@
class AddSourceToSeeds < ActiveRecord::Migration[7.2]
def change
add_column :seeds, :source, :string
add_index :seeds, :source
end
end

View File

@@ -0,0 +1,52 @@
class AddIndexesCrops < ActiveRecord::Migration[7.2]
def change
add_index :alternate_names, :crop_id
add_index :alternate_names, :creator_id
add_index :alternate_names, :language
add_index :comments, :author_id
add_index :crop_companions, %i(crop_a_id crop_b_id)
add_index :crops, :creator_id
add_index :crops, :parent_id
add_index :follows, %i(follower_id followed_id)
add_index :forums, :owner_id
add_index :harvests, :crop_id
add_index :harvests, :owner_id
add_index :harvests, :plant_part_id
add_index :members_roles, %i(member_id role_id)
add_index :notifications, :sender_id
add_index :notifications, :recipient_id
add_index :orders_products, %i(order_id product_id)
add_index :photo_associations, :crop_id # TODO: Is this still in use?
add_index :photos, :owner_id
add_index :photos, :source_id
add_index :photos_plantings, %i(photo_id planting_id)
add_index :plant_parts, :slug, unique: true
add_index :plantings, :crop_id
add_index :plantings, :garden_id
add_index :plantings, :owner_id
add_index :plantings, :parent_seed_id
add_index :posts, :forum_id
add_index :scientific_names, :crop_id
add_index :scientific_names, :creator_id
add_index :seeds, :owner_id
add_index :seeds, :crop_id
add_index :seeds, :parent_planting_id
end
end

View File

@@ -0,0 +1,5 @@
class AddOverallRatingPlantings < ActiveRecord::Migration[7.2]
def change
add_column :plantings, :overall_rating, :integer
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
ActiveRecord::Schema[7.2].define(version: 2025_09_01_130830) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -68,6 +68,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "language"
t.index ["creator_id"], name: "index_alternate_names_on_creator_id"
t.index ["crop_id"], name: "index_alternate_names_on_crop_id"
t.index ["language"], name: "index_alternate_names_on_language"
end
create_table "authentications", id: :serial, force: :cascade do |t|
@@ -209,6 +212,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "commentable_type"
t.index ["author_id"], name: "index_comments_on_author_id"
t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id"
end
create_table "crop_companions", force: :cascade do |t|
@@ -216,6 +221,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "crop_b_id", null: false
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.index ["crop_a_id", "crop_b_id"], name: "index_crop_companions_on_crop_a_id_and_crop_b_id"
end
create_table "crop_posts", id: false, force: :cascade do |t|
@@ -246,7 +252,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.jsonb "openfarm_data"
t.integer "harvests_count", default: 0
t.integer "photo_associations_count", default: 0
t.index ["creator_id"], name: "index_crops_on_creator_id"
t.index ["name"], name: "index_crops_on_name"
t.index ["parent_id"], name: "index_crops_on_parent_id"
t.index ["requester_id"], name: "index_crops_on_requester_id"
t.index ["slug"], name: "index_crops_on_slug", unique: true
end
@@ -256,6 +264,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "followed_id"
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.index ["follower_id", "followed_id"], name: "index_follows_on_follower_id_and_followed_id"
end
create_table "forums", id: :serial, force: :cascade do |t|
@@ -265,6 +274,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil
t.datetime "updated_at", precision: nil
t.string "slug"
t.index ["owner_id"], name: "index_forums_on_owner_id"
t.index ["slug"], name: "index_forums_on_slug", unique: true
end
@@ -328,6 +338,9 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.float "si_weight"
t.integer "planting_id"
t.integer "likes_count", default: 0
t.index ["crop_id"], name: "index_harvests_on_crop_id"
t.index ["owner_id"], name: "index_harvests_on_owner_id"
t.index ["plant_part_id"], name: "index_harvests_on_plant_part_id"
t.index ["planting_id"], name: "index_harvests_on_planting_id"
end
@@ -464,6 +477,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
create_table "members_roles", id: false, force: :cascade do |t|
t.integer "member_id"
t.integer "role_id"
t.index ["member_id", "role_id"], name: "index_members_roles_on_member_id_and_role_id"
end
create_table "notifications", id: :serial, force: :cascade do |t|
@@ -477,11 +491,14 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "updated_at", precision: nil
t.string "notifiable_type"
t.index ["notifiable_type", "notifiable_id"], name: "index_notifications_on_notifiable_type_and_notifiable_id"
t.index ["recipient_id"], name: "index_notifications_on_recipient_id"
t.index ["sender_id"], name: "index_notifications_on_sender_id"
end
create_table "orders_products", id: false, force: :cascade do |t|
t.integer "order_id"
t.integer "product_id"
t.index ["order_id", "product_id"], name: "index_orders_products_on_order_id_and_product_id"
end
create_table "photo_associations", id: :serial, force: :cascade do |t|
@@ -491,6 +508,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "created_at", precision: nil, null: false
t.datetime "updated_at", precision: nil, null: false
t.integer "crop_id"
t.index ["crop_id"], name: "index_photo_associations_on_crop_id"
t.index ["photographable_id", "photographable_type", "photo_id"], name: "items_to_photos_idx", unique: true
t.index ["photographable_id", "photographable_type"], name: "photographable_idx"
end
@@ -511,12 +529,15 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.string "source"
t.integer "comments_count", default: 0
t.index ["fullsize_url"], name: "index_photos_on_fullsize_url", unique: true
t.index ["owner_id"], name: "index_photos_on_owner_id"
t.index ["source_id"], name: "index_photos_on_source_id"
t.index ["thumbnail_url"], name: "index_photos_on_thumbnail_url", unique: true
end
create_table "photos_plantings", id: false, force: :cascade do |t|
t.integer "photo_id"
t.integer "planting_id"
t.index ["photo_id", "planting_id"], name: "index_photos_plantings_on_photo_id_and_planting_id"
end
create_table "photos_seeds", id: false, force: :cascade do |t|
@@ -531,6 +552,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.datetime "updated_at", precision: nil
t.string "slug"
t.integer "harvests_count", default: 0
t.index ["slug"], name: "index_plant_parts_on_slug", unique: true
end
create_table "plantings", id: :serial, force: :cascade do |t|
@@ -554,6 +576,11 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "harvests_count", default: 0
t.integer "likes_count", default: 0
t.boolean "failed", default: false, null: false
t.integer "overall_rating"
t.index ["crop_id"], name: "index_plantings_on_crop_id"
t.index ["garden_id"], name: "index_plantings_on_garden_id"
t.index ["owner_id"], name: "index_plantings_on_owner_id"
t.index ["parent_seed_id"], name: "index_plantings_on_parent_seed_id"
t.index ["slug"], name: "index_plantings_on_slug", unique: true
end
@@ -568,6 +595,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.integer "likes_count", default: 0
t.integer "comments_count", default: 0
t.index ["created_at", "author_id"], name: "index_posts_on_created_at_and_author_id"
t.index ["forum_id"], name: "index_posts_on_forum_id"
t.index ["slug"], name: "index_posts_on_slug", unique: true
end
@@ -590,6 +618,8 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.string "gbif_rank"
t.string "gbif_status"
t.string "wikidata_id"
t.index ["creator_id"], name: "index_scientific_names_on_creator_id"
t.index ["crop_id"], name: "index_scientific_names_on_crop_id"
end
create_table "seeds", id: :serial, force: :cascade do |t|
@@ -611,7 +641,12 @@ ActiveRecord::Schema[7.2].define(version: 2025_08_24_162600) do
t.date "finished_at"
t.integer "parent_planting_id"
t.date "saved_at"
t.string "source"
t.index ["crop_id"], name: "index_seeds_on_crop_id"
t.index ["owner_id"], name: "index_seeds_on_owner_id"
t.index ["parent_planting_id"], name: "index_seeds_on_parent_planting_id"
t.index ["slug"], name: "index_seeds_on_slug", unique: true
t.index ["source"], name: "index_seeds_on_source"
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"

View File

@@ -2,7 +2,7 @@
require 'rails_helper'
describe "footer", :js do
describe "footer" do
before { visit root_path }
it "footer is on home page" do

View File

@@ -198,6 +198,7 @@ describe "Planting a crop", :js, :search do
within "form#new_planting" do
fill_in "When?", with: "2014-07-01"
check "Mark as finished"
find_by_id('planting_overall_rating').set 4
fill_in "Finished date", with: "2014-08-30"
uncheck 'Mark as finished'
end
@@ -220,6 +221,7 @@ describe "Planting a crop", :js, :search do
expect(page).to have_content "planting was successfully created"
expect(page).to have_content "Finished"
expect(page).to have_content "Aug 2014"
expect(page).to have_content "4/5"
# ensure we've indexed in elastic search
planting.reindex(refresh: true)

View File

@@ -49,6 +49,7 @@ describe "seeds", :js do
click_link 'Edit'
expect(page).to have_current_path edit_seed_path(seed), ignore_query: true
fill_in 'Quantity', with: seed.quantity * 2
select 'traded from another person', from: 'Source'
click_button 'Save'
expect(page).to have_current_path seed_path(seed), ignore_query: true
end

View File

@@ -2,7 +2,7 @@
require 'rails_helper'
describe "signin", :js do
describe "signin" do
let(:member) { FactoryBot.create(:member) }
let(:recipient) { FactoryBot.create(:member) }
let(:wrangler) { FactoryBot.create(:crop_wrangling_member) }

View File

@@ -2,7 +2,7 @@
require 'rails_helper'
describe "signup", :js do
describe "signup" do
it "sign up for new account from top menubar" do
visit crops_path # something other than front page, which has multiple signup links
click_link 'Sign up'

View File

@@ -17,6 +17,10 @@ describe "timeline", :js do
let!(:friend_harvest) { FactoryBot.create(:planting, owner: friend2, planted_at: 3.years.ago) }
let!(:finished_planting) { FactoryBot.create(:finished_planting, owner: friend1) }
let!(:no_planted_at_planting) { FactoryBot.create(:planting, owner: friend2, planted_at: nil) }
let!(:friend_photo) { FactoryBot.create(:photo, owner: friend1) }
let!(:friend_post) { FactoryBot.create(:post, author: friend2) }
let!(:liked_post) { FactoryBot.create(:like, likeable: friend_photo, member: friend2) }
let!(:liked_photo) { FactoryBot.create(:like, likeable: friend_post, member: friend1) }
before do
login_as(member)
@@ -28,6 +32,8 @@ describe "timeline", :js do
it { expect(page).to have_link href: planting_path(friend_harvest) }
it { expect(page).to have_link href: planting_path(finished_planting) }
it { expect(page).to have_no_link href: planting_path(no_planted_at_planting) }
it { expect(page).to have_link href: photo_path(friend_photo) }
it { expect(page).to have_link href: post_path(friend_post) }
end
describe 'shows the friends you follow' do