diff --git a/app/controllers/shared/trips_controller.rb b/app/controllers/shared/trips_controller.rb index 9b583295..cc3f8c62 100644 --- a/app/controllers/shared/trips_controller.rb +++ b/app/controllers/shared/trips_controller.rb @@ -7,11 +7,8 @@ class Shared::TripsController < ApplicationController redirect_to root_path, alert: 'Shared trip not found or no longer available' and return unless @trip&.public_accessible? @user = @trip.user - @is_public_view = true - @coordinates = @trip.path.present? ? extract_coordinates : [] - @photo_previews = @trip.share_photos? ? fetch_photo_previews : [] - - render 'trips/public_show' + @coordinates = extract_coordinates + @photo_previews = fetch_photo_previews end private @@ -24,6 +21,8 @@ class Shared::TripsController < ApplicationController end def fetch_photo_previews + return [] unless @trip.share_photos? + Rails.cache.fetch("trip_photos_#{@trip.id}", expires_in: 1.day) do @trip.photo_previews end diff --git a/app/controllers/trips_controller.rb b/app/controllers/trips_controller.rb index 2818b4af..d60b7ef9 100644 --- a/app/controllers/trips_controller.rb +++ b/app/controllers/trips_controller.rb @@ -39,8 +39,8 @@ class TripsController < ApplicationController end def update - # Handle sharing settings update (JSON response) - update_sharing and return if params[:sharing] + # Handle sharing settings update + handle_sharing_update if params[:sharing] # Handle regular trip update if @trip.update(trip_params) @@ -68,38 +68,18 @@ class TripsController < ApplicationController ).map { [_1.to_f, _2.to_f, _3.to_s, _4.to_s, _5.to_s, _6.to_s, _7.to_s, _8.to_s] } end - def update_sharing + def handle_sharing_update if params[:sharing][:enabled] == '1' sharing_options = { - expiration: params[:sharing][:expiration] || '24h' + expiration: params[:sharing][:expiration] || '24h', + share_notes: params[:sharing][:share_notes] == '1', + share_photos: params[:sharing][:share_photos] == '1' } - # Add optional sharing settings - sharing_options[:share_notes] = params[:sharing][:share_notes] == '1' - sharing_options[:share_photos] = params[:sharing][:share_photos] == '1' - @trip.enable_sharing!(**sharing_options) - sharing_url = shared_trip_url(@trip.sharing_uuid) - - render json: { - success: true, - sharing_url: sharing_url, - message: 'Sharing enabled successfully' - } else @trip.disable_sharing! - - render json: { - success: true, - message: 'Sharing disabled successfully' - } end - rescue StandardError => e - render json: { - success: false, - message: 'Failed to update sharing settings', - error: e.message - }, status: :unprocessable_content end def trip_params diff --git a/app/javascript/controllers/public_trip_map_controller.js b/app/javascript/controllers/shared/trip_map_controller.js similarity index 97% rename from app/javascript/controllers/public_trip_map_controller.js rename to app/javascript/controllers/shared/trip_map_controller.js index 49f0b27f..f1a3f61a 100644 --- a/app/javascript/controllers/public_trip_map_controller.js +++ b/app/javascript/controllers/shared/trip_map_controller.js @@ -1,5 +1,5 @@ import L from "leaflet"; -import BaseController from "./base_controller"; +import BaseController from "../base_controller"; export default class extends BaseController { static values = { diff --git a/app/views/trips/public_show.html.erb b/app/views/shared/trips/show.html.erb similarity index 95% rename from app/views/trips/public_show.html.erb rename to app/views/shared/trips/show.html.erb index 04d7620d..74acf17b 100644 --- a/app/views/trips/public_show.html.erb +++ b/app/views/shared/trips/show.html.erb @@ -54,9 +54,9 @@ <% if @coordinates.present? %>
+ data-controller="shared--trip-map" + data-shared--trip-map-coordinates-value="<%= @coordinates.to_json %>" + data-shared--trip-map-name-value="<%= @trip.name %>"> <% else %>
diff --git a/app/views/trips/_sharing.html.erb b/app/views/trips/_sharing.html.erb index 6c51b733..69437e5a 100644 --- a/app/views/trips/_sharing.html.erb +++ b/app/views/trips/_sharing.html.erb @@ -5,7 +5,7 @@ Generate a public link to share this trip with others.

-
+ <%= form_with model: trip, url: trip_path(trip), method: :patch, data: { turbo: false } do |f| %> <% if trip.sharing_enabled? %>
@@ -73,16 +73,11 @@
- - + <%= f.submit "Disable Sharing", + name: 'sharing[enabled]', + value: '0', + class: "btn btn-error flex-1", + data: { turbo_confirm: "Are you sure you want to disable sharing for this trip?" } %>
<% else %> @@ -92,118 +87,40 @@ - + <%= f.select 'sharing[expiration]', + options_for_select([ + ['1 hour', '1h'], + ['12 hours', '12h'], + ['24 hours', '24h'], + ['Never (permanent)', 'permanent'] + ], '24h'), + {}, + class: 'select select-bordered' %>
- + <%= f.hidden_field 'sharing[enabled]', value: '1' %> + <%= f.submit "Enable Sharing", class: "btn btn-primary w-full" %>
<% end %> -
+ <% end %> - - diff --git a/spec/models/concerns/shareable_spec.rb b/spec/models/concerns/shareable_spec.rb deleted file mode 100644 index cc97bc7c..00000000 --- a/spec/models/concerns/shareable_spec.rb +++ /dev/null @@ -1,213 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Shareable do - let(:user) { create(:user) } - let(:trip) do - create(:trip, user: user, name: 'Test Trip', - started_at: 1.week.ago, ended_at: Time.current) - end - - describe '#generate_sharing_uuid' do - it 'generates a UUID before create' do - new_trip = build(:trip, user: user) - expect(new_trip.sharing_uuid).to be_nil - new_trip.save! - expect(new_trip.sharing_uuid).to be_present - expect(new_trip.sharing_uuid).to match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/) - end - end - - describe '#sharing_enabled?' do - it 'returns false by default' do - expect(trip.sharing_enabled?).to be false - end - - it 'returns true when enabled' do - trip.update!(sharing_settings: { 'enabled' => true }) - expect(trip.sharing_enabled?).to be true - end - - it 'returns false when disabled' do - trip.update!(sharing_settings: { 'enabled' => false }) - expect(trip.sharing_enabled?).to be false - end - end - - describe '#sharing_expired?' do - it 'returns false when no expiration is set' do - expect(trip.sharing_expired?).to be false - end - - it 'returns false when expires_at is in the future' do - trip.update!(sharing_settings: { - 'expiration' => '24h', - 'expires_at' => 1.hour.from_now.iso8601 - }) - expect(trip.sharing_expired?).to be false - end - - it 'returns true when expires_at is in the past' do - trip.update!(sharing_settings: { - 'expiration' => '1h', - 'expires_at' => 1.hour.ago.iso8601 - }) - expect(trip.sharing_expired?).to be true - end - - it 'returns true when expiration is set but expires_at is missing' do - trip.update!(sharing_settings: { - 'expiration' => '24h', - 'expires_at' => nil - }) - expect(trip.sharing_expired?).to be true - end - end - - describe '#public_accessible?' do - it 'returns false by default' do - expect(trip.public_accessible?).to be false - end - - it 'returns true when enabled and not expired' do - trip.update!(sharing_settings: { - 'enabled' => true, - 'expiration' => '24h', - 'expires_at' => 1.hour.from_now.iso8601 - }) - expect(trip.public_accessible?).to be true - end - - it 'returns false when enabled but expired' do - trip.update!(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.hour.ago.iso8601 - }) - expect(trip.public_accessible?).to be false - end - - it 'returns false when disabled' do - trip.update!(sharing_settings: { - 'enabled' => false, - 'expiration' => '24h', - 'expires_at' => 1.hour.from_now.iso8601 - }) - expect(trip.public_accessible?).to be false - end - end - - describe '#enable_sharing!' do - it 'enables sharing with default 24h expiration' do - trip.enable_sharing! - expect(trip.sharing_enabled?).to be true - expect(trip.sharing_settings['expiration']).to eq('24h') - expect(trip.sharing_settings['expires_at']).to be_present - end - - it 'enables sharing with custom expiration' do - trip.enable_sharing!(expiration: '1h') - expect(trip.sharing_enabled?).to be true - expect(trip.sharing_settings['expiration']).to eq('1h') - end - - it 'enables sharing with permanent expiration' do - trip.enable_sharing!(expiration: 'permanent') - expect(trip.sharing_enabled?).to be true - expect(trip.sharing_settings['expiration']).to eq('permanent') - expect(trip.sharing_settings['expires_at']).to be_nil - end - - it 'defaults to 24h for invalid expiration' do - trip.enable_sharing!(expiration: 'invalid') - expect(trip.sharing_settings['expiration']).to eq('24h') - end - - it 'stores additional options like share_notes' do - trip.enable_sharing!(expiration: '24h', share_notes: true) - expect(trip.sharing_settings['share_notes']).to be true - end - - it 'stores additional options like share_photos' do - trip.enable_sharing!(expiration: '24h', share_photos: true) - expect(trip.sharing_settings['share_photos']).to be true - end - - it 'generates a sharing_uuid if not present' do - trip.update_column(:sharing_uuid, nil) - trip.enable_sharing! - expect(trip.sharing_uuid).to be_present - end - - it 'keeps existing sharing_uuid' do - original_uuid = trip.sharing_uuid - trip.enable_sharing! - expect(trip.sharing_uuid).to eq(original_uuid) - end - end - - describe '#disable_sharing!' do - before do - trip.enable_sharing!(expiration: '24h') - end - - it 'disables sharing' do - trip.disable_sharing! - expect(trip.sharing_enabled?).to be false - end - - it 'clears expiration settings' do - trip.disable_sharing! - expect(trip.sharing_settings['expiration']).to be_nil - expect(trip.sharing_settings['expires_at']).to be_nil - end - - it 'keeps the sharing_uuid' do - original_uuid = trip.sharing_uuid - trip.disable_sharing! - expect(trip.sharing_uuid).to eq(original_uuid) - end - end - - describe '#generate_new_sharing_uuid!' do - it 'generates a new UUID' do - original_uuid = trip.sharing_uuid - trip.generate_new_sharing_uuid! - expect(trip.sharing_uuid).not_to eq(original_uuid) - expect(trip.sharing_uuid).to be_present - end - end - - describe '#share_notes?' do - it 'returns false by default' do - expect(trip.share_notes?).to be false - end - - it 'returns true when share_notes is enabled' do - trip.update!(sharing_settings: { 'share_notes' => true }) - expect(trip.share_notes?).to be true - end - - it 'returns false when share_notes is disabled' do - trip.update!(sharing_settings: { 'share_notes' => false }) - expect(trip.share_notes?).to be false - end - end - - describe '#share_photos?' do - it 'returns false by default' do - expect(trip.share_photos?).to be false - end - - it 'returns true when share_photos is enabled' do - trip.update!(sharing_settings: { 'share_photos' => true }) - expect(trip.share_photos?).to be true - end - - it 'returns false when share_photos is disabled' do - trip.update!(sharing_settings: { 'share_photos' => false }) - expect(trip.share_photos?).to be false - end - end -end diff --git a/spec/models/stat_spec.rb b/spec/models/stat_spec.rb index 6b70b59b..1cfabfe4 100644 --- a/spec/models/stat_spec.rb +++ b/spec/models/stat_spec.rb @@ -263,222 +263,8 @@ RSpec.describe Stat, type: :model do end end - describe 'sharing settings' do - let(:user) { create(:user) } - let(:stat) { create(:stat, year: 2024, month: 6, user: user) } - - describe '#sharing_enabled?' do - context 'when sharing_settings is nil' do - before { stat.update_column(:sharing_settings, nil) } - - it 'returns false' do - expect(stat.sharing_enabled?).to be false - end - end - - context 'when sharing_settings is empty hash' do - before { stat.update(sharing_settings: {}) } - - it 'returns false' do - expect(stat.sharing_enabled?).to be false - end - end - - context 'when enabled is false' do - before { stat.update(sharing_settings: { 'enabled' => false }) } - - it 'returns false' do - expect(stat.sharing_enabled?).to be false - end - end - - context 'when enabled is true' do - before { stat.update(sharing_settings: { 'enabled' => true }) } - - it 'returns true' do - expect(stat.sharing_enabled?).to be true - end - end - - context 'when enabled is a string "true"' do - before { stat.update(sharing_settings: { 'enabled' => 'true' }) } - - it 'returns false (strict boolean check)' do - expect(stat.sharing_enabled?).to be false - end - end - end - - describe '#sharing_expired?' do - context 'when sharing_settings is nil' do - before { stat.update_column(:sharing_settings, nil) } - - it 'returns false' do - expect(stat.sharing_expired?).to be false - end - end - - context 'when expiration is blank' do - before { stat.update(sharing_settings: { 'enabled' => true }) } - - it 'returns false' do - expect(stat.sharing_expired?).to be false - end - end - - context 'when expiration is present but expires_at is blank' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h' - }) - end - - it 'returns true' do - expect(stat.sharing_expired?).to be true - end - end - - context 'when expires_at is in the future' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.hour.from_now.iso8601 - }) - end - - it 'returns false' do - expect(stat.sharing_expired?).to be false - end - end - - context 'when expires_at is in the past' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.hour.ago.iso8601 - }) - end - - it 'returns true' do - expect(stat.sharing_expired?).to be true - end - end - - context 'when expires_at is 1 second in the future' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.second.from_now.iso8601 - }) - end - - it 'returns false (not yet expired)' do - expect(stat.sharing_expired?).to be false - end - end - - context 'when expires_at is invalid date string' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 'invalid-date' - }) - end - - it 'returns true (treats as expired)' do - expect(stat.sharing_expired?).to be true - end - end - - context 'when expires_at is nil' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => nil - }) - end - - it 'returns true' do - expect(stat.sharing_expired?).to be true - end - end - - context 'when expires_at is empty string' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => '' - }) - end - - it 'returns true' do - expect(stat.sharing_expired?).to be true - end - end - end - - describe '#public_accessible?' do - context 'when sharing_settings is nil' do - before { stat.update_column(:sharing_settings, nil) } - - it 'returns false' do - expect(stat.public_accessible?).to be false - end - end - - context 'when sharing is not enabled' do - before { stat.update(sharing_settings: { 'enabled' => false }) } - - it 'returns false' do - expect(stat.public_accessible?).to be false - end - end - - context 'when sharing is enabled but expired' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.hour.ago.iso8601 - }) - end - - it 'returns false' do - expect(stat.public_accessible?).to be false - end - end - - context 'when sharing is enabled and not expired' do - before do - stat.update(sharing_settings: { - 'enabled' => true, - 'expiration' => '1h', - 'expires_at' => 1.hour.from_now.iso8601 - }) - end - - it 'returns true' do - expect(stat.public_accessible?).to be true - end - end - - context 'when sharing is enabled with no expiration' do - before do - stat.update(sharing_settings: { 'enabled' => true }) - end - - it 'returns true' do - expect(stat.public_accessible?).to be true - end - end - end + describe 'Shareable concern' do + it_behaves_like 'shareable' end end end diff --git a/spec/models/trip_spec.rb b/spec/models/trip_spec.rb index e42f0573..05aa9c94 100644 --- a/spec/models/trip_spec.rb +++ b/spec/models/trip_spec.rb @@ -156,36 +156,12 @@ RSpec.describe Trip, type: :model do end describe 'Shareable concern' do - let(:user) { create(:user) } - let(:trip) { create(:trip, user: user) } + it_behaves_like 'shareable' - describe 'sharing_uuid generation' do - it 'generates a sharing_uuid on create' do - new_trip = build(:trip, user: user) - expect(new_trip.sharing_uuid).to be_nil - new_trip.save! - expect(new_trip.sharing_uuid).to be_present - end - end + describe 'Trip-specific sharing' do + let(:user) { create(:user) } + let(:trip) { create(:trip, user: user) } - describe '#public_accessible?' do - it 'returns false by default' do - expect(trip.public_accessible?).to be false - end - - it 'returns true when sharing is enabled and not expired' do - trip.enable_sharing!(expiration: '24h') - expect(trip.public_accessible?).to be true - end - - it 'returns false when sharing is disabled' do - trip.enable_sharing!(expiration: '24h') - trip.disable_sharing! - expect(trip.public_accessible?).to be false - end - end - - describe '#enable_sharing!' do it 'enables sharing with notes and photos options' do trip.enable_sharing!(expiration: '24h', share_notes: true, share_photos: true) expect(trip.sharing_enabled?).to be true diff --git a/spec/requests/shared/trips_spec.rb b/spec/requests/shared/trips_spec.rb index f301c611..a58ed3dd 100644 --- a/spec/requests/shared/trips_spec.rb +++ b/spec/requests/shared/trips_spec.rb @@ -35,7 +35,7 @@ RSpec.describe 'Shared::Trips', type: :request do expect(response.body).to include('Test Trip') expect(response.body).to include('Trip Route') - expect(response.body).to include('data-controller="public-trip-map"') + expect(response.body).to include('data-controller="shared--trip-map"') expect(response.body).to include(trip.sharing_uuid) end @@ -117,17 +117,12 @@ RSpec.describe 'Shared::Trips', type: :request do before { sign_in user } context 'enabling sharing' do - it 'enables sharing and returns success' do + it 'enables sharing and redirects to trip' do patch trip_path(trip), - params: { sharing: { enabled: '1', expiration: '24h' } }, - as: :json + params: { sharing: { enabled: '1', expiration: '24h' } } - expect(response).to have_http_status(:success) - - json_response = JSON.parse(response.body) - expect(json_response['success']).to be(true) - expect(json_response['sharing_url']).to be_present - expect(json_response['message']).to eq('Sharing enabled successfully') + expect(response).to redirect_to(trip_path(trip)) + expect(flash[:notice]).to eq('Trip was successfully updated.') trip.reload expect(trip.sharing_enabled?).to be(true) @@ -136,10 +131,9 @@ RSpec.describe 'Shared::Trips', type: :request do it 'enables sharing with notes option' do patch trip_path(trip), - params: { sharing: { enabled: '1', expiration: '24h', share_notes: '1' } }, - as: :json + params: { sharing: { enabled: '1', expiration: '24h', share_notes: '1' } } - expect(response).to have_http_status(:success) + expect(response).to redirect_to(trip_path(trip)) trip.reload expect(trip.sharing_enabled?).to be(true) @@ -148,10 +142,9 @@ RSpec.describe 'Shared::Trips', type: :request do it 'enables sharing with photos option' do patch trip_path(trip), - params: { sharing: { enabled: '1', expiration: '24h', share_photos: '1' } }, - as: :json + params: { sharing: { enabled: '1', expiration: '24h', share_photos: '1' } } - expect(response).to have_http_status(:success) + expect(response).to redirect_to(trip_path(trip)) trip.reload expect(trip.sharing_enabled?).to be(true) @@ -160,10 +153,9 @@ RSpec.describe 'Shared::Trips', type: :request do it 'sets custom expiration when provided' do patch trip_path(trip), - params: { sharing: { enabled: '1', expiration: '1h' } }, - as: :json + params: { sharing: { enabled: '1', expiration: '1h' } } - expect(response).to have_http_status(:success) + expect(response).to redirect_to(trip_path(trip)) trip.reload expect(trip.sharing_enabled?).to be(true) expect(trip.sharing_settings['expiration']).to eq('1h') @@ -171,10 +163,9 @@ RSpec.describe 'Shared::Trips', type: :request do it 'enables permanent sharing' do patch trip_path(trip), - params: { sharing: { enabled: '1', expiration: 'permanent' } }, - as: :json + params: { sharing: { enabled: '1', expiration: 'permanent' } } - expect(response).to have_http_status(:success) + expect(response).to redirect_to(trip_path(trip)) trip.reload expect(trip.sharing_settings['expiration']).to eq('permanent') expect(trip.sharing_settings['expires_at']).to be_nil @@ -186,16 +177,12 @@ RSpec.describe 'Shared::Trips', type: :request do trip.enable_sharing!(expiration: '24h') end - it 'disables sharing and returns success' do + it 'disables sharing and redirects to trip' do patch trip_path(trip), - params: { sharing: { enabled: '0' } }, - as: :json + params: { sharing: { enabled: '0' } } - expect(response).to have_http_status(:success) - - json_response = JSON.parse(response.body) - expect(json_response['success']).to be(true) - expect(json_response['message']).to eq('Sharing disabled successfully') + expect(response).to redirect_to(trip_path(trip)) + expect(flash[:notice]).to eq('Trip was successfully updated.') trip.reload expect(trip.sharing_enabled?).to be(false) @@ -205,8 +192,7 @@ RSpec.describe 'Shared::Trips', type: :request do context 'when trip does not exist' do it 'returns not found' do patch trip_path(id: 999999), - params: { sharing: { enabled: '1' } }, - as: :json + params: { sharing: { enabled: '1' } } expect(response).to have_http_status(:not_found) end @@ -218,8 +204,7 @@ RSpec.describe 'Shared::Trips', type: :request do it 'returns not found' do patch trip_path(other_trip), - params: { sharing: { enabled: '1' } }, - as: :json + params: { sharing: { enabled: '1' } } expect(response).to have_http_status(:not_found) end @@ -229,8 +214,7 @@ RSpec.describe 'Shared::Trips', type: :request do context 'when user is not signed in' do it 'returns unauthorized' do patch trip_path(trip), - params: { sharing: { enabled: '1' } }, - as: :json + params: { sharing: { enabled: '1' } } expect(response).to have_http_status(:unauthorized) end diff --git a/spec/support/shared_examples/shareable_examples.rb b/spec/support/shared_examples/shareable_examples.rb new file mode 100644 index 00000000..d438bf08 --- /dev/null +++ b/spec/support/shared_examples/shareable_examples.rb @@ -0,0 +1,199 @@ +# frozen_string_literal: true + +RSpec.shared_examples 'shareable' do + let(:user) { create(:user) } + let(:shareable_model) { described_class.name.underscore.to_sym } + let(:shareable) { create(shareable_model, user: user) } + + describe '#generate_sharing_uuid' do + it 'generates a UUID before create' do + new_record = build(shareable_model, user: user) + expect(new_record.sharing_uuid).to be_nil + new_record.save! + expect(new_record.sharing_uuid).to be_present + expect(new_record.sharing_uuid).to match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/) + end + end + + describe '#sharing_enabled?' do + it 'returns false by default' do + expect(shareable.sharing_enabled?).to be false + end + + it 'returns true when enabled' do + shareable.update!(sharing_settings: { 'enabled' => true }) + expect(shareable.sharing_enabled?).to be true + end + + it 'returns false when disabled' do + shareable.update!(sharing_settings: { 'enabled' => false }) + expect(shareable.sharing_enabled?).to be false + end + end + + describe '#sharing_expired?' do + it 'returns false when no expiration is set' do + expect(shareable.sharing_expired?).to be false + end + + it 'returns false when expires_at is in the future' do + shareable.update!(sharing_settings: { + 'expiration' => '24h', + 'expires_at' => 1.hour.from_now.iso8601 + }) + expect(shareable.sharing_expired?).to be false + end + + it 'returns true when expires_at is in the past' do + shareable.update!(sharing_settings: { + 'expiration' => '1h', + 'expires_at' => 1.hour.ago.iso8601 + }) + expect(shareable.sharing_expired?).to be true + end + + it 'returns true when expiration is set but expires_at is missing' do + shareable.update!(sharing_settings: { + 'expiration' => '24h', + 'expires_at' => nil + }) + expect(shareable.sharing_expired?).to be true + end + end + + describe '#public_accessible?' do + it 'returns false by default' do + expect(shareable.public_accessible?).to be false + end + + it 'returns true when enabled and not expired' do + shareable.update!(sharing_settings: { + 'enabled' => true, + 'expiration' => '24h', + 'expires_at' => 1.hour.from_now.iso8601 + }) + expect(shareable.public_accessible?).to be true + end + + it 'returns false when enabled but expired' do + shareable.update!(sharing_settings: { + 'enabled' => true, + 'expiration' => '1h', + 'expires_at' => 1.hour.ago.iso8601 + }) + expect(shareable.public_accessible?).to be false + end + + it 'returns false when disabled' do + shareable.update!(sharing_settings: { + 'enabled' => false, + 'expiration' => '24h', + 'expires_at' => 1.hour.from_now.iso8601 + }) + expect(shareable.public_accessible?).to be false + end + end + + describe '#enable_sharing!' do + it 'enables sharing with default 24h expiration' do + shareable.enable_sharing! + expect(shareable.sharing_enabled?).to be true + expect(shareable.sharing_settings['expiration']).to eq('24h') + expect(shareable.sharing_settings['expires_at']).to be_present + end + + it 'enables sharing with custom expiration' do + shareable.enable_sharing!(expiration: '1h') + expect(shareable.sharing_enabled?).to be true + expect(shareable.sharing_settings['expiration']).to eq('1h') + end + + it 'enables sharing with permanent expiration' do + shareable.enable_sharing!(expiration: 'permanent') + expect(shareable.sharing_enabled?).to be true + expect(shareable.sharing_settings['expiration']).to eq('permanent') + expect(shareable.sharing_settings['expires_at']).to be_nil + end + + it 'defaults to 24h for invalid expiration' do + shareable.enable_sharing!(expiration: 'invalid') + expect(shareable.sharing_settings['expiration']).to eq('24h') + end + + it 'generates a sharing_uuid if not present' do + shareable.update_column(:sharing_uuid, nil) + shareable.enable_sharing! + expect(shareable.sharing_uuid).to be_present + end + + it 'keeps existing sharing_uuid' do + original_uuid = shareable.sharing_uuid + shareable.enable_sharing! + expect(shareable.sharing_uuid).to eq(original_uuid) + end + end + + describe '#disable_sharing!' do + before do + shareable.enable_sharing!(expiration: '24h') + end + + it 'disables sharing' do + shareable.disable_sharing! + expect(shareable.sharing_enabled?).to be false + end + + it 'clears expiration settings' do + shareable.disable_sharing! + expect(shareable.sharing_settings['expiration']).to be_nil + expect(shareable.sharing_settings['expires_at']).to be_nil + end + + it 'keeps the sharing_uuid' do + original_uuid = shareable.sharing_uuid + shareable.disable_sharing! + expect(shareable.sharing_uuid).to eq(original_uuid) + end + end + + describe '#generate_new_sharing_uuid!' do + it 'generates a new UUID' do + original_uuid = shareable.sharing_uuid + shareable.generate_new_sharing_uuid! + expect(shareable.sharing_uuid).not_to eq(original_uuid) + expect(shareable.sharing_uuid).to be_present + end + end + + describe '#share_notes?' do + it 'returns false by default' do + expect(shareable.share_notes?).to be false + end + + it 'returns true when share_notes is enabled' do + shareable.update!(sharing_settings: { 'share_notes' => true }) + expect(shareable.share_notes?).to be true + end + + it 'returns false when share_notes is disabled' do + shareable.update!(sharing_settings: { 'share_notes' => false }) + expect(shareable.share_notes?).to be false + end + end + + describe '#share_photos?' do + it 'returns false by default' do + expect(shareable.share_photos?).to be false + end + + it 'returns true when share_photos is enabled' do + shareable.update!(sharing_settings: { 'share_photos' => true }) + expect(shareable.share_photos?).to be true + end + + it 'returns false when share_photos is disabled' do + shareable.update!(sharing_settings: { 'share_photos' => false }) + expect(shareable.share_photos?).to be false + end + end +end