Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/channels/application_cable/channel.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
4 changes: 4 additions & 0 deletions app/channels/application_cable/connection.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
end
end
15 changes: 13 additions & 2 deletions app/controllers/urls_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ def create
@url.user = current_user if user_signed_in?

if @url.save
broadcast_url_to_user(@url)
if user_signed_in?
redirect_to root_path, notice: "Short URL created successfully!"
else
Expand Down Expand Up @@ -60,8 +61,8 @@ def lengthen
@url.short_code = generate_long_code

if @url.save
long_url = short_url_for(@url)
render json: { long_url: long_url }
broadcast_url_to_user(@url)
render json: { long_url: short_url_for(@url) }
else
render json: { error: @url.errors.full_messages.first }, status: :unprocessable_entity
end
Expand Down Expand Up @@ -163,6 +164,16 @@ def throttle_expand!
render json: { error: "Too many requests. Please slow down." }, status: :too_many_requests
end

def broadcast_url_to_user(url)
return unless url.user_id.present?
Turbo::StreamsChannel.broadcast_prepend_to(
"urls_#{url.user_id}",
target: "urls_list",
partial: "urls/url",
locals: { url: url }
)
end

def permitted_url_params
allowed = [:original_url]
allowed << :short_code if user_signed_in?
Expand Down
27 changes: 27 additions & 0 deletions app/views/urls/_url.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<% kind = url.short_code.length > 20 ? "long" : "short" %>
<div class="link-card" data-url-id="<%= url.id %>" data-kind="<%= kind %>">
<%# Left: URL info — always visible %>
<div class="link-card-body">
<div class="link-card-top">
<%= link_to short_url_for(url), redirect_short_url_path(url.short_code),
target: "_blank", rel: "noopener noreferrer", class: "link-short" %>
<button class="copy-btn" data-url="<%= short_url_for(url) %>">Copy</button>
</div>
<%= link_to truncate(url.original_url, length: 72), url.original_url,
target: "_blank", rel: "noopener noreferrer", class: "link-original",
title: url.original_url %>
</div>

<%# Right: stats + actions %>
<div class="link-card-side">
<span class="link-clicks"><%= url.clicks %> <%= url.clicks == 1 ? "click" : "clicks" %></span>
<span class="link-date"><%= url.created_at.strftime("%b %d") %></span>
<div class="link-actions">
<%= link_to "Stats", analytics_url_path(url), class: "link-action" %>
<%= link_to "QR", qr_code_url_path(url), class: "link-action" %>
<%= link_to "Delete", url_path(url),
class: "link-action link-action-delete",
data: { turbo_method: :delete, turbo_confirm: "Delete this link?" } %>
</div>
</div>
</div>
42 changes: 6 additions & 36 deletions app/views/urls/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -103,42 +103,17 @@
</div>
</div>

<div class="links-list">
<%= turbo_stream_from "urls_#{current_user.id}" %>
<div class="links-list" id="urls_list">
<% @urls.each do |url| %>
<% kind = url.short_code.length > 20 ? "long" : "short" %>
<div class="link-card" data-url-id="<%= url.id %>" data-kind="<%= kind %>">
<%# Left: URL info — always visible %>
<div class="link-card-body">
<div class="link-card-top">
<%= link_to short_url_for(url), redirect_short_url_path(url.short_code),
target: "_blank", rel: "noopener noreferrer", class: "link-short" %>
<button class="copy-btn" data-url="<%= short_url_for(url) %>">Copy</button>
</div>
<%= link_to truncate(url.original_url, length: 72), url.original_url,
target: "_blank", rel: "noopener noreferrer", class: "link-original",
title: url.original_url %>
</div>

<%# Right: stats + actions
Desktop: stats always, actions on hover
Mobile: wraps to full-width bottom bar, actions always visible %>
<div class="link-card-side">
<span class="link-clicks"><%= url.clicks %> <%= url.clicks == 1 ? "click" : "clicks" %></span>
<span class="link-date"><%= url.created_at.strftime("%b %d") %></span>
<div class="link-actions">
<%= link_to "Stats", analytics_url_path(url), class: "link-action" %>
<%= link_to "QR", qr_code_url_path(url), class: "link-action" %>
<%= link_to "Delete", url_path(url),
class: "link-action link-action-delete",
data: { turbo_method: :delete, turbo_confirm: "Delete this link?" } %>
</div>
</div>
</div>
<%= render "url", url: url %>
<% end %>
</div>
</section>

<% elsif user_signed_in? %>
<%= turbo_stream_from "urls_#{current_user.id}" %>
<div class="links-list" id="urls_list"></div>
<div class="empty-state">
<p>No links yet — paste a URL above to get started.</p>
</div>
Expand Down Expand Up @@ -348,12 +323,7 @@
});
const data = await resp.json();
if (data.error) throw new Error(data.error);

resultUrl.href = data.long_url;
resultUrl.textContent = data.long_url;
copyBtn.dataset.url = data.long_url;
result.hidden = false;
setTimeout(() => window.location.reload(), 2000);
window.location.reload();
} catch (e) {
errEl.textContent = e.message;
errEl.hidden = false;
Expand Down