Emi Matchu
aa0b376a12
In particular, we got feedback that it was surprising to not get to check which NeoPass you wanted to use, and that the permissions were never prompted again. I figure let's err on the side of ample clarity! As part of this, I've added the new `external_link_icon` global helper, which embeds an SVG from Chakra UI. That's just the convenient place I know to grab that icon, and I did it this way instead of an `img` tag because that enables the `currentColor` thing to work instead of coming out black!
260 lines
7.3 KiB
Ruby
260 lines
7.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 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
|
|
|
|
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)",
|
|
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.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
|
|
|
|
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
|
|
|