Matchu
a007479f99
We were previously planning a more interesting "Add to Cart" integration with TNT, but it hasn't panned out! For now, we'll just link to the NC Mall homepage.
304 lines
9.3 KiB
Ruby
304 lines
9.3 KiB
Ruby
module ApplicationHelper
|
|
include FragmentLocalization
|
|
|
|
def absolute_url(path_or_url)
|
|
if path_or_url.include?('://') # already an absolute URL
|
|
path_or_url
|
|
else # a relative path
|
|
request.protocol + request.host_with_port + path_or_url
|
|
end
|
|
end
|
|
|
|
def add_body_class(class_name)
|
|
@body_class ||= ''
|
|
@body_class << " #{class_name}"
|
|
end
|
|
|
|
def body_class
|
|
controller = params[:controller].gsub("/", "-")
|
|
"#{controller} #{controller}-#{params[:action]}".tap do |output|
|
|
output << @body_class if @body_class
|
|
end
|
|
end
|
|
|
|
def button_link_to(content_or_url, url = nil, icon: nil, **options)
|
|
if url.present?
|
|
content = content_or_url
|
|
url = url
|
|
else
|
|
content = nil
|
|
url = content_or_url
|
|
end
|
|
|
|
klass = options.fetch(:class, "") + " button"
|
|
link_to url, class: klass, **options do
|
|
concat icon
|
|
concat " "
|
|
if block_given?
|
|
yield
|
|
else
|
|
concat content
|
|
end
|
|
end
|
|
end
|
|
|
|
def advertise_campaign_progress(campaign, &block)
|
|
if campaign && campaign.advertised?
|
|
campaign_progress(campaign, &block)
|
|
end
|
|
end
|
|
|
|
def cents_to_currency(cents, options={})
|
|
number_to_currency(cents / 100.0, options)
|
|
end
|
|
|
|
def campaign_progress(campaign, &block)
|
|
if campaign
|
|
if block_given?
|
|
content = capture(&block)
|
|
else
|
|
if campaign.complete?
|
|
pitch = "We've met this year's fundraising goal! Thanks, everybody!"
|
|
prompt = "Learn more"
|
|
elsif campaign.remaining < 200_00
|
|
estimate = (campaign.remaining.to_f / 10_00).ceil * 10_00
|
|
if estimate == campaign.remaining
|
|
pitch = "We're only #{cents_to_currency(estimate, precision: 0)} away from paying #{campaign.purpose}!"
|
|
else
|
|
pitch = "We're less than #{cents_to_currency(estimate, precision: 0)} away from paying #{campaign.purpose}!"
|
|
end
|
|
prompt = "Donate now"
|
|
else
|
|
pitch = "Help Dress to Impress stay online!"
|
|
prompt = "Learn more"
|
|
end
|
|
content = link_to(
|
|
content_tag(:span, pitch) +
|
|
content_tag(:span, prompt, :class => 'button'), donate_path)
|
|
end
|
|
|
|
meter = content_tag(:div, nil, :class => 'campaign-progress',
|
|
style: "width: #{campaign.progress_percent}%;")
|
|
label = content_tag(:div, content, :class => 'campaign-progress-label')
|
|
content_tag(:div, meter + label, :class => 'campaign-progress-wrapper')
|
|
end
|
|
end
|
|
|
|
def canonical_path(resource)
|
|
I18n.with_locale(I18n.default_locale) do
|
|
content_for :meta, tag(:link, :rel => 'canonical', :href => url_for(resource))
|
|
end
|
|
end
|
|
|
|
# SVG icon source from Font Awesome 6! (https://creativecommons.org/licenses/by/4.0/)
|
|
CART_ICON_SVG_SOURCE = '<path fill="currentColor" d="M0 24C0 10.7 10.7 0 24 0H69.5c22 0 41.5 12.8 50.6 32h411c26.3 0 45.5 25 38.6 50.4l-41 152.3c-8.5 31.4-37 53.3-69.5 53.3H170.7l5.4 28.5c2.2 11.3 12.1 19.5 23.6 19.5H488c13.3 0 24 10.7 24 24s-10.7 24-24 24H199.7c-34.6 0-64.3-24.6-70.7-58.5L77.4 54.5c-.7-3.8-4-6.5-7.9-6.5H24C10.7 48 0 37.3 0 24zM128 464a48 48 0 1 1 96 0 48 48 0 1 1 -96 0zm336-48a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"></path>'.html_safe
|
|
def cart_icon(alt: "Cart")
|
|
content_tag :svg, CART_ICON_SVG_SOURCE, alt:, class: "icon",
|
|
viewBox: "0 0 576 512", style: "height: 1em"
|
|
end
|
|
|
|
def contact_email
|
|
"matchu@openneo.net"
|
|
end
|
|
|
|
# SVG icon source from Chakra UI!
|
|
EXTERNAL_LINK_SVG_SOURCE = '<g fill="none" stroke="currentColor" stroke-linecap="round" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path></g>'.html_safe
|
|
def external_link_icon
|
|
content_tag :svg, EXTERNAL_LINK_SVG_SOURCE, alt: "(external link)",
|
|
class: "icon", viewBox: "0 0 24 24", style: "width: 1em; height: 1em"
|
|
end
|
|
|
|
def flashes
|
|
raw(flash.inject('') do |html, pair|
|
|
key, value = pair
|
|
html + content_tag('p', value, :class => "flash #{key}")
|
|
end)
|
|
end
|
|
|
|
def hide_home_link
|
|
@hide_home_link = true
|
|
end
|
|
|
|
def home_link?
|
|
!@hide_home_link
|
|
end
|
|
|
|
def support_staff?
|
|
user_signed_in? && current_user.support_staff?
|
|
end
|
|
|
|
def impress_2020_meta_tags
|
|
origin = Rails.configuration.impress_2020_origin
|
|
support_secret = Rails.application.credentials.dig(
|
|
:impress_2020, :support_secret)
|
|
|
|
capture do
|
|
concat tag("meta", name: "impress-2020-origin", content: origin)
|
|
|
|
if support_staff? && support_secret.present?
|
|
concat tag("meta", name: "impress-2020-support-secret",
|
|
content: support_secret)
|
|
end
|
|
end
|
|
end
|
|
|
|
JAVASCRIPT_LIBRARIES = {
|
|
:jquery => 'https://ajax.googleapis.com/ajax/libs/jquery/1.4.3/jquery.min.js',
|
|
:jquery_tmpl => 'https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js',
|
|
}
|
|
|
|
def include_javascript_libraries(*library_names)
|
|
raw(library_names.inject('') do |html, name|
|
|
html + javascript_include_tag(JAVASCRIPT_LIBRARIES[name], defer: true)
|
|
end)
|
|
end
|
|
|
|
def locale_options
|
|
current_locale_is_public = false
|
|
options = I18n.public_locales.map do |available_locale|
|
|
current_locale_is_public = true if I18n.locale == available_locale
|
|
# Include fallbacks data on the tag. Right now it's used in blog
|
|
# localization, but may conceivably be used for something else later.
|
|
[translate('locale_name', :locale => available_locale), available_locale,
|
|
{'data-fallbacks' => I18n.fallbacks[available_locale].join(',')}]
|
|
end
|
|
|
|
unless current_locale_is_public
|
|
name = translate('locale_name', :locale => I18n.locale) + ' (alpha)'
|
|
options << [name, I18n.locale]
|
|
end
|
|
|
|
options
|
|
end
|
|
|
|
def localized_cache(key={}, &block)
|
|
localized_key = localize_fragment_key(key, locale)
|
|
# TODO: The digest feature is handy, but it's not compatible with how we
|
|
# check for fragments existence in the controller, so skip it for now.
|
|
cache(localized_key, skip_digest: true, &block)
|
|
end
|
|
|
|
def auth_user_sign_in_path_with_return_to
|
|
new_auth_user_session_path :return_to => request.fullpath
|
|
end
|
|
|
|
def origin_tag(value)
|
|
hidden_field_tag 'origin', value, :id => nil
|
|
end
|
|
|
|
def open_graph(properties)
|
|
if @open_graph
|
|
@open_graph.merge! properties
|
|
else
|
|
@open_graph = properties
|
|
end
|
|
end
|
|
|
|
def open_graph_tags
|
|
if @open_graph
|
|
@open_graph.inject('') do |output, property|
|
|
key, value = property
|
|
output + tag(:meta, :property => "og:#{key}", :content => value)
|
|
end.html_safe
|
|
end
|
|
end
|
|
|
|
# Includes a stylesheet designed for this specific page of the app, which
|
|
# should be removed when navigating to another page. We use Turbo's
|
|
# `data-turbo-track="dynamic"` option to do this.
|
|
def page_stylesheet_link_tag(src, options={})
|
|
options = {data: {"turbo-track": "dynamic"}}.deep_merge(options)
|
|
stylesheet_link_tag src, options
|
|
end
|
|
|
|
# SVG icon source from Chakra UI!
|
|
SEARCH_SVG_SOURCE = '<path fill="currentColor" d="M23.384,21.619,16.855,15.09a9.284,9.284,0,1,0-1.768,1.768l6.529,6.529a1.266,1.266,0,0,0,1.768,0A1.251,1.251,0,0,0,23.384,21.619ZM2.75,9.5a6.75,6.75,0,1,1,6.75,6.75A6.758,6.758,0,0,1,2.75,9.5Z"></path>'.html_safe
|
|
def search_icon
|
|
content_tag :svg, SEARCH_SVG_SOURCE, alt: "Search", class: "icon",
|
|
viewBox: "0 0 24 24", style: "width: 1em; height: 1em"
|
|
end
|
|
|
|
def secondary_nav(&block)
|
|
content_for :before_flashes,
|
|
content_tag(:nav, :id => 'secondary-nav', &block)
|
|
end
|
|
|
|
def show_title_header?
|
|
params[:controller] != 'items' && !@hide_title_header
|
|
end
|
|
|
|
def hide_title_header
|
|
@hide_title_header = true
|
|
end
|
|
|
|
def signed_in_meta_tag
|
|
%(<meta name="user-signed-in" content="#{user_signed_in?}">).html_safe
|
|
end
|
|
|
|
def current_user_id_meta_tag
|
|
%(<meta name="current-user-id" content="#{current_user.id}">).html_safe
|
|
end
|
|
|
|
def labeled_time_ago_in_words(time)
|
|
content_tag :abbr, time_ago_in_words(time), :title => time
|
|
end
|
|
|
|
def title(value)
|
|
content_for :title, value
|
|
end
|
|
|
|
def md(text)
|
|
RDiscount.new(text).to_html.html_safe
|
|
end
|
|
|
|
def terms_updated_at
|
|
Date.new(2024, 2, 29)
|
|
end
|
|
|
|
def terms_updated_timestamp
|
|
terms_updated_at.strftime("%b %Y")
|
|
end
|
|
|
|
def terms_updated_recently
|
|
terms_updated_at >= 2.months.ago
|
|
end
|
|
|
|
def translate_markdown(key, options={})
|
|
md translate("#{key}_markdown", **options)
|
|
end
|
|
|
|
alias_method :tmd, :translate_markdown
|
|
|
|
def translate_with_links(key, options={})
|
|
nonlink_options = {}
|
|
link_urls = {}
|
|
|
|
options.each do |key, value|
|
|
str_key = key.to_s
|
|
if str_key.end_with? '_link_url'
|
|
link_key = str_key[0..-5] # "abcdef_link_url" => "abcdef_link"
|
|
link_urls[link_key] = value
|
|
else
|
|
nonlink_options[key] = value
|
|
end
|
|
end
|
|
|
|
link_options = {}
|
|
link_urls.each do |link_key, url|
|
|
content = translate("#{key}.#{link_key}_content", **nonlink_options)
|
|
link_options[link_key.to_sym] = link_to(content, url)
|
|
end
|
|
|
|
converted_options = link_options.merge(nonlink_options)
|
|
translate("#{key}.main_html", **converted_options)
|
|
end
|
|
|
|
alias_method :twl, :translate_with_links
|
|
|
|
def userbar_contributions_summary(user)
|
|
translate_with_links '.userbar.contributions_summary',
|
|
:contributions_link_url => user_contributions_path(user),
|
|
:user_points => user.points
|
|
end
|
|
end
|
|
|