Website landing page revamp

What a language! The syntax is a mix of several languages (like PL1) !!!

Personally, I would not add the horizontal grey line (just above Download button).
It seems “too much things” on a screen.
Rem: there are 2 download buttons.

Ignore the grey bar near the top, its to show what is above-the-fold (ie visible without scrolling), similarly there’ll be no black edges.

Maybe I should remove all that stuff to remove any confusion.

So if we scroll, we get part1 or part2 (only 1 button visible) => OK
No you can let the grey bar, just add the info.

Hi webDev,

I really like the look of it, excellent!

From the functionality aspect the page allocates much space for the download option (available twice) and relatively little space to “Latest apps” (the only part where a regular user would find new information).

I prefer “added last week” and “added last month” (or “added since”) over “more apps”.
The latter is probably what nowaday’s users would expect but it conveys less information than the former versions and feels addictive to me;
The user just taps on “more” (mental effort is low) and as there’s no time information the user does not know whether he/she already has scrolled into previously seen/evaluated information - by this you keep him on the site (as you’d expect from a commercial site. Which wants to keep the users on its site - instead of getting the primary interest done (find a new app and use it (thus leaving the site))).
Also “more apps” offers little/no info about how many apps entered recently and does not suggest a recommended reading interval (e.g. week/month).
(yes, I read the book “Hooked: How to Build Habit-Forming Products” :slight_smile:

Hi @hotlittlewhitedog. I removed the black bar (see above). Please ignore the black on the top and sides, they are overflow that will never be seen.

Thanks. If you are a trusted contributor then that gives me some confidence to move forward (EDIT: move forward with coding this into the site. If you are not an official contributor then it still gives me some confidence, thanks). Given your concerns about the word “more” I have changed it to “next”.

EDIT: the “next” buttonwill go down to the newspaper section as mentioned above (in the edited comment). The way the list of new apps is generated, I cannot say “this month’s apps” or “this week” etc

Please see the edited desktop website above. I will attach the mobile version of the site in about 20 minutes so please stay tuned.

Here’s the mobile version.

If people are happy then that’s great, if you are not an official contributor please tag one that you know.

I will need the go-ahead from at least a couple official contributors so that I’m not wasting time as it could take some time to implement.

If you want to make changes, now is the time to tell me them.


I’ve done a quick search and found that a non-breaking hyphen exists and that we need to use it whenever we write F-Droid.


I’ve noticed that the current website doesn’t use a non-breaking hyphen for the “dash” (‑) character in F‑Droid, therefore there’s a 1-in-20 (approx) chance of “F-” appearing at the end of a line and “Droid” appearing on the next line.

During the above revamp I’ll fix this across the site if people agree.

For example, I wonder if we should setup a global variable $botName and that will output to F‑Droid

GO FOR PROD :smiley:

I agree, it’s better.

Hi @hotlittlewhitedog,

Good to hear from you, thanks. I’m moving forward now.

I’m trying to complete the setup of a very simple process whereby a contributor only needs to change one variable and put a folder in their localhost in order for them to get a massive speed boost that I’ve been able to get by using a massively truncated database.

Basically, I’d like it to be very simple, (ie. they only need to uncomment the following line in _config.yml:

# fdroid-repo: http://localhost/f-droid-website-testing-resources

It appears that the jekyll-fdroid/lib/fdroid/IndexV1.rb file doesn’t share the same scope as other Ruby files because when I use the following new code…

    # Website testing repo location (should match the location specified in '_config.yml')
    $fdroids_website_testing_localhost = "http://localhost/f-droid-website-testing-resources"
    $fdroids_website_testing_mode = false
    if site.config["fdroid-repo"] == $fdroids_website_testing_localhost
      $fdroids_website_testing_mode = true

I get the following error at build time.

Gem Load Error is: undefined local variable or method `site' for FDroid::IndexV1:Class

Is there an elegant way to solve this or would a person need to change two parts of the code in two completely different files? (@hotlittlewhitedog, @hans , @Licaon_Kter)

Either you didn’t start the good script (variable defined ahead) or you didn’t give the site value as call parameter. The best is to search “site” in all scripts with grep and see where the var is set.


grep site *.gem --color

Thank you - I do not have that status so the latter will have to do:)

Actually my focus was on functionality and not at all about the word (sorry if this was not clear).

Let’s take a look on a weekly visitor: Between Dec, 28th and Jan 4th there were 107 app updated.
The current landing page on lists exactly three of them (with no option to see more). This is broken by design (and this affects developers, users and contributors alike (apps never make it to landing page, updates are missed, work behind the scenes underestimated)).

If a “more” or “next” (I don’t care) functionality is added the user needs additional help to avoid loosing orientation. That’s why I suggested “last week/month” (there might be cleverer solutions than that). From Dec, 4th to Jan 4th there were 397 updates so there really is some traffic there.

Thanks @hotlittlewhitedog, what do you mean when you say…

I found what produced the site variable. It’s the variable context below. (Note also that the class that failed previously ( IndexV1) is “required” before site is instantiated (you were right!):

require_relative '../fdroid/IndexV1'

module Jekyll
  # Used to output the repo name/timestamp used to generate this F-Droid site.
  class FDroidRepoInfoTag < Liquid::Tag
    @@repotag = ''

    def initialize(tag_name, text, tokens)

    def render(context)
      if @@repotag == ''
        site = context.registers[:site]

        url = site.config['fdroid-repo']
        index =, 'en')
        @@repotag = "#{} #{}"
      return @@repotag

The context variable is supposedly an array of args. I saw that and immediately thought that I could use the *args variable (or pointer?) in the same way that render() above was called (ie. by providing a single parameter *args). I was wrong. Not *args nor args is a variable that I can use in F-Droid’s IndexV1 class.

It must be a variable unique to some plugin or module, because I tried a web search and nothing about an args variable (I thought it might be like the args[] array in Java).

Wow. That is impressive! I had no idea the database (in terms of updates) was growing this fast!

Firstly, in the “Newspaper section” I should now put “Updated apps” above “New apps” because the “new” section that you helped produce (near the top of the page) already mentions 3 new apps. An easy change.

I think a new user will not want too many apps shown on the landing page. Information overload and all that. Maybe some text to say “(325 this month)” and “(+19 this month)” for the “Updated” and “New” sections respectively is something that will be of great interest to users, rather than just an invitation to view the list. I could probably implement that fairly easily.

I agree there needs to be two pages to browse ‘new’ and ‘updated’ apps. That’s why I put the “(+) more” links in the design, but I’ll remove those to add “(+N this month)” links instead.

Hi @webDev
args is a list of arguments (like in C, Bash).

“didn’t give the site value as call parameter.”
=> it’s the call (internal call of a function/procedure or external call with its list of parameter(s).

Hi @hotlittlewhitedog,

that’s was my first thought, yes, I haven’t had a good chance to work out where the args are being produced from. I thought the _config.yml file would somehow provide it. I’m confused how one plugin has access but other don’t. I need to take a step back and understand the whole architecture of the site. If I remember correctly there are resources available already to understand it so will try to solve it.

Unfortunately busy yesterday and today but will report back when and if needed.


Okay. So I noticed the Repo class tonight.

Strangely the repo.address() method returns:

…rather than:

…no matter what I seem to do.

  • Tried changing the _config.yml
  • Tried changing the index_v1.json database entry for repo address.

When 'grep’ping “”, the string really doesn’t seem to appear anywhere else that seems substantial.

I feel like this should be easy, I’m missing one thing fundamental and maybe important, aren’t I?

Yop @webDev ,

Here is all code of the project: jekyll-fdroid where ‘site’ appears:

fab@fabuntu:~/Git/jekyll-fdroid$ grep -rin 'site' * --exclude=*.log | grep -v .json
assets/fdroid-search-autocomplete.js:258:     * @param baseurl The site.baseurl variable from Jekyll.
assets/fdroid-search-autocomplete.js:259:     * @param fdroidRepo The site.fdroid-repo variable from Jekyll.
assets/fdroid-search-autocomplete.js:280:     * @param baseurl The site.baseurl variable from Jekyll.
assets/fdroid-search-autocomplete.js:281:     * @param fdroidRepo The site.fdroid-repo variable from Jekyll.
assets/register-listener.js:12:        '{{ site.baseurl }}',
assets/register-listener.js:13:        '{{ site.fdroid-repo }}',
bower_components/awesomplete/ official site for the library is at <>.
bower_components/mustache.js/ make a website
bower_components/mustache.js/ you need a template for a dynamic part in a static website, you can consider including the template in the static HTML file to avoid loading templates separately. Here's a small example using `jQuery`:
bower_components/lunr.js/test/env/chai.js:387:   * Sets the `any` flag, (opposite of the `all` flag)
bower_components/lunr.js/test/env/chai.js:406:   * Sets the `all` flag (opposite of the `any` flag)
_includes/package-list-item.html:1:<a class="package-header" href="{{ include.package.beautiful_url | prepend: site.baseurl }}">
_includes/package-list-item.html:2:	<img class="package-icon" src="{{ site.fdroid-repo }}/{{ include.package.icon }}" />
_includes/search-full-default-result-template.html:1:<a class="package-header" href="{{ site.baseurl }}/packages/{{ packageName }}/">
_includes/browse-paginator-navigation.html:7:			<a class="label" href="{{ include.paginator.previous_page_path | prepend: site.baseurl }}">&lt;</a>
_includes/browse-paginator-navigation.html:15:			<a class="label" href="{{ include.permalink | append: '..' | prepend: site.baseurl }}">1</a>
_includes/browse-paginator-navigation.html:25:				<a class="label" href="{{ trail.path | prepend: site.baseurl }}">{{ trail.num }}</a>
_includes/browse-paginator-navigation.html:39:			<a class="label" href="{{ include.permalink | append: back | append: include.paginator.total_pages | prepend: site.baseurl }}">{{include.paginator.total_pages}}</a>
_includes/browse-paginator-navigation.html:45:			<a class="label" href="{{ include.paginator.next_page_path | prepend: site.baseurl }}">&gt;</a>
_layouts/package.html:5:{% assign strings = %}
_layouts/package.html:11:			src="{{ site.fdroid-repo}}/{{ page.package_name }}/{{ page.feature_graphic }}" />
_layouts/package.html:15:		<img class="package-icon" src="{{ site.fdroid-repo }}/{{ page.icon }}" />
_layouts/package.html:33:		{% if != "" and != nil %}
_layouts/package.html:35:				<a href="{{ }}">{{ }}</a>
_layouts/package.html:72:					<li class="screenshot"><img src="{{ site.fdroid-repo}}/{{ page.package_name }}/{{ screenshot }}" /></li>
_layouts/package.html:83:		<a href="" class="material-button">{{ }}</a>
_layouts/package.html:102:					<a href="{{ site.fdroid-repo }}/{{ package.apk_name }}">{{ strings.download_apk }}</a> {{ package.size|file_size_human_readable }}
_layouts/package.html:103:					<a href="{{ site.fdroid-repo }}/{{ package.apk_name }}.asc">{{ strings.gpg_signature }}</a>
_layouts/sidebar-latest-packages.html:1:{% assign latestPackages = site.packages | sort: "title" | reverse | sort: "added" | reverse %}
_layouts/search-autocomplete.html:1:<script src="{{ site.baseurl }}/js/lunr.js"></script>
_layouts/search-autocomplete.html:2:<script src="{{ site.baseurl }}/js/mustache.min.js"></script>
_layouts/search-autocomplete.html:3:<script src="{{ site.baseurl }}/js/awesomplete.min.js"></script>
_layouts/search-autocomplete.html:4:<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete.js"></script>
_layouts/search-autocomplete.html:5:<link href="{{ site.baseurl }}/js/awesomplete.css" rel="stylesheet" type="text/css" />
_layouts/search-autocomplete.html:14:    data-baseurl="{{ site.baseurl }}"
_layouts/search-autocomplete.html:15:    data-fdroid-repo="{{ site.fdroid-repo }}"
_layouts/search-autocomplete.html:18:<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete-init.js"></script>
_layouts/sidebar-lastupdated-packages.html:1:{% assign updatedPackages = site.packages | sort: "title" | reverse | sort: "last_updated" | reverse %}
_layouts/search-full.html:1:<script src="{{ site.baseurl }}/js/lunr.js"></script>
_layouts/search-full.html:2:<script src="{{ site.baseurl }}/js/mustache.min.js"></script>
_layouts/search-full.html:3:<script src="{{ site.baseurl }}/js/awesomplete.min.js"></script>
_layouts/search-full.html:4:<script src="{{ site.baseurl }}/js/fdroid-search-autocomplete.js"></script>
_layouts/search-full.html:5:<script src="{{ site.baseurl }}/js/register-listener.js"></script>
_layouts/search-full.html:6:<link href="{{ site.baseurl }}/js/awesomplete.css" rel="stylesheet" type="text/css" />
lib/lunr/LunrIndexer.rb:18:      # @param [Jekyll::Site]  site
lib/lunr/LunrIndexer.rb:21:      def generate(site, packages)
lib/lunr/LunrIndexer.rb:42:        @site = site
lib/lunr/LunrIndexer.rb:61:        FileUtils.mkdir_p(File.join(site.dest, @js_dir))
lib/lunr/LunrIndexer.rb:69:        filepath = File.join(site.dest, filename)
lib/lunr/LunrIndexer.rb:74:        site_js = File.join(site.dest, @js_dir)
lib/lunr/LunrIndexer.rb:87:          Jekyll.logger.debug "Lunr:", "Copying asset from #{src} to #{site_js}"
lib/lunr/LunrIndexer.rb:88:          FileUtils.cp(src, site_js)
lib/lunr/LunrIndexer.rb:94:          site.static_files <<, site.dest, "/", filename)
lib/jekyll/FDroidPackageDetailGenerator.rb:25:    def generate(site)
lib/jekyll/FDroidPackageDetailGenerator.rb:30:        # Add plugin's SASS directory so site's list of SASS directories
lib/jekyll/FDroidPackageDetailGenerator.rb:31:        if site.config["sass"].nil? || site.config["sass"].empty?
lib/jekyll/FDroidPackageDetailGenerator.rb:32:          site.config["sass"] =
lib/jekyll/FDroidPackageDetailGenerator.rb:34:        if site.config["sass"]["load_paths"].nil? || site.config["sass"]["load_paths"].empty?
lib/jekyll/FDroidPackageDetailGenerator.rb:35:          site.config["sass"]["load_paths"] = ["_sass", (File.expand_path "../../_sass", File.dirname(__FILE__))]
lib/jekyll/FDroidPackageDetailGenerator.rb:37:          site.config["sass"]["load_paths"] << (File.expand_path "../../_sass", File.dirname(__FILE__))
lib/jekyll/FDroidPackageDetailGenerator.rb:41:        if site.config["pagination"].nil? || site.config["pagination"].empty?
lib/jekyll/FDroidPackageDetailGenerator.rb:42:          site.config["pagination"] =
lib/jekyll/FDroidPackageDetailGenerator.rb:44:        site.config["pagination"]["enabled"] = true
lib/jekyll/FDroidPackageDetailGenerator.rb:46:        index =["fdroid-repo"], site.active_lang || 'en_US')
lib/jekyll/FDroidPackageDetailGenerator.rb:48:, index.apps)
lib/jekyll/FDroidPackageDetailGenerator.rb:51:        site.collections["packages"] =, "packages")
lib/jekyll/FDroidPackageDetailGenerator.rb:53:          # This page needs to be created twice, once for site.pages, and once for site.collections.
lib/jekyll/FDroidPackageDetailGenerator.rb:58:          site.pages <<, site.source, package)
lib/jekyll/FDroidPackageDetailGenerator.rb:59:          site.collections["packages"].docs <<, site.source, package)
lib/jekyll/FDroidPackageDetailGenerator.rb:62:        site.includes_load_paths << (File.expand_path "../../_includes", File.dirname(__FILE__))
lib/jekyll/FDroidPackageDetailGenerator.rb:63:        site.pages <<, site.source)
lib/jekyll/FDroidBrowsingPage.rb:20:    def initialize(site, base)
lib/jekyll/FDroidBrowsingPage.rb:21:      @site = site
lib/jekyll/FDroidPackageDetailPage.rb:20:    # @param [Jekyll::Site]  site
lib/jekyll/FDroidPackageDetailPage.rb:23:    def initialize(site, base, package)
lib/jekyll/FDroidPackageDetailPage.rb:24:      @site = site
lib/jekyll/FDroidPackageDetailPage.rb:35:      layout_dir_override = File.join(site.source, '_layouts')
lib/jekyll/ReadYamlPage.rb:25:        self.content =, name.to_s), (site ? site.file_read_opts : {}).merge(opts))
lib/jekyll/FDroidSearchAutocompleteTag.rb:21:      site = context.registers[:site]
lib/jekyll/FDroidSearchAutocompleteTag.rb:22:      repo_timestamp =['fdroid-repo'], 'en').repo.timestamp
lib/jekyll/FDroidRepoInfoTag.rb:21:  # Used to output the repo name/timestamp used to generate this F-Droid site.
lib/jekyll/FDroidRepoInfoTag.rb:31:        site = context.registers[:site]
lib/jekyll/FDroidRepoInfoTag.rb:32:        url = site.config['fdroid-repo']
lib/fdroid/App.rb:88:        'author_website' => field('authorWebSite'),
lib/fdroid/App.rb:103:        'website' => field('webSite'), this gem you can browse packages of a F-Droid repository in a Jekyll site.
_sass/jekyll-fdroid.scss:40:.package-header, .package-header:visited {
spec/lib/fdroid/FDroidIndex_spec.rb:221:        "author_website" => nil,
spec/lib/fdroid/FDroidIndex_spec.rb:235:        "website" => "",
spec/lib/fdroid/FDroidRepoInfoTag_spec.rb:13:          :site =>

Search in all codes where you have something like this:

lib/jekyll/FDroidBrowsingPage.rb:20:    def initialize(site, base)
lib/jekyll/FDroidBrowsingPage.rb:21:      @site = site
spec/lib/fdroid/FDroidRepoInfoTag_spec.rb:13:          :site =>

On 20: site is used as parameter
On 21: site array? (I think, I don’t know ruby but perl), site is set.
On 13: in spec files

Here when :site is found, a new instance of Jekill::Site is created, with attributes:
(file: /spec/lib/fdroid/FDroidRepoInfoTag_spec.rb)

 :site =>
              'source' => '/tmp',
              'destination' => '/tmp/build',
              'permalink' => '',
              'liquid' => {
                'error_mode' => ''
              'limit_posts' => 0,
              'plugins' => [],
              'kramdown' => {},
              'fdroid-repo' => ''

Your initial question:

url = site.config['fdroid-repo']
index =, 'en')
@@repotag = "#{} #{}"

Let’s look in FDroid::IndexV1 .
One download method calling another download method, and finally download real files, zip…
I hope its clear :slight_smile :slight_smile:

module FDroid
  class IndexV1
    attr_reader :apps, :repo

    @@downloaded_repos = {}

    # Download and parse an index, returning a new instance of IndexV1.
    # @param [string]  repo
    # @param [string]  locale
    # @return [FDroid::IndexV1]
>  def, locale)
      repo = URI.parse "#{repo}/index-v1.jar"
>    index = download_index repo, locale)

    # Make a network request, download the index-v1.jar file from the repo, unzip and get the contents
    # of the index-v1.json file.
    # @param [string]  repo
    # @return [Hash]
>  def self.download_index(repo)
      if @@downloaded_repos.has_key? repo
>        return @@downloaded_repos[repo]

      Dir.mktmpdir do |dir|
        jar = File.join dir, 'index-v1.jar'
        open(jar, 'wb') do |file|
          rescue Net::OpenTimeout, Net::ReadTimeout => e
            puts "Timeout (#{e}), retrying in 1 second..."
        end do |zip_file|
          entry = zip_file.glob('index-v1.json').first
          @@downloaded_repos[repo] =
          next @@downloaded_repos[repo]

    def initialize(index, locale)
      @apps = index['apps'].map do |app_json|
        packages_json = index['packages'][app_json['packageName']], packages_json, locale)

      @repo =['repo'])

Hi @hotlittlewhitedog,

Thanks! Your first reply looks like it was most helpful. I don’t know how I missed this!

I just opened FDroidBrowsingPage.rb and it inherits from ReadYamlPage, lol

class FDroidBrowsingPage < ReadYamlPage

If only I searched for yaml instead of config.yml haha

Yes, that part was. I already hacked at this to make my json file work. Now i’m just building an elegant solution so future devs only need to uncomment one line of the yml and place a folder in their localhost to imitate contacting the real repo. :slight_smile: You’ll see how simple I’ve made it soon.