diff --git a/app/controllers/api/v1/timelines/local_top_controller.rb b/app/controllers/api/v1/timelines/local_top_controller.rb
new file mode 100644
index 0000000000..fd11a3a24a
--- /dev/null
+++ b/app/controllers/api/v1/timelines/local_top_controller.rb
@@ -0,0 +1,10 @@
+module Api::V1::Timelines
+ class LocalTopController < ApiController
+ before_action :require_user!
+
+ def show
+ @statuses = LocalTopPostsService.new.call
+ render json: @statuses
+ end
+ end
+ end
\ No newline at end of file
diff --git a/app/javascript/mastodon/components/navigation_portal.tsx b/app/javascript/mastodon/components/navigation_portal.tsx
index d3ac8baa6e..9904b70f2b 100644
--- a/app/javascript/mastodon/components/navigation_portal.tsx
+++ b/app/javascript/mastodon/components/navigation_portal.tsx
@@ -1,6 +1,16 @@
import Trends from 'mastodon/features/getting_started/containers/trends_container';
import { showTrends } from 'mastodon/initial_state';
+import { Link } from 'react-router-dom';
export const NavigationPortal: React.FC = () => (
-
{showTrends && }
-);
+
+ {/* Existing Trends section */}
+ {showTrends && }
+
+ {/* Add Local Top tab */}
+
+
+ Local Top
+
+
+);
\ No newline at end of file
diff --git a/app/javascript/mastodon/features/local_top_timeline/index.js b/app/javascript/mastodon/features/local_top_timeline/index.js
new file mode 100644
index 0000000000..e23334cd9d
--- /dev/null
+++ b/app/javascript/mastodon/features/local_top_timeline/index.js
@@ -0,0 +1,14 @@
+// app/javascript/mastodon/features/local_top_timeline/index.js
+import React from 'react';
+import Timeline from '../../components/timeline';
+
+export default class LocalTopTimeline extends React.PureComponent {
+ render() {
+ return (
+
+ );
+ }
+}
\ No newline at end of file
diff --git a/app/javascript/mastodon/routes.js b/app/javascript/mastodon/routes.js
new file mode 100644
index 0000000000..b52f401472
--- /dev/null
+++ b/app/javascript/mastodon/routes.js
@@ -0,0 +1,6 @@
+// app/javascript/mastodon/routes.js
+{
+ path: '/timelines/local_top',
+ component: AsyncComponent(() => import('../features/local_top_timeline')),
+ exact: true,
+ }
\ No newline at end of file
diff --git a/app/services/local_top_posts_service.rb b/app/services/local_top_posts_service.rb
new file mode 100644
index 0000000000..530ad15521
--- /dev/null
+++ b/app/services/local_top_posts_service.rb
@@ -0,0 +1,15 @@
+class LocalTopPostsService < BaseService
+ # Rank local posts by engagement (boosts > favs > replies) in last 24h
+ def call
+ Status
+ .local
+ .joins(:status_stat)
+ .where('statuses.created_at > ?', 24.hours.ago)
+ .select('statuses.*,
+ (status_stats.reblogs_count * 0.6 +
+ status_stats.favourites_count * 0.3 +
+ status_stats.replies_count * 0.1) AS engagement_score')
+ .order('engagement_score DESC')
+ .limit(50) # Adjust as needed
+ end
+ end
\ No newline at end of file
diff --git a/app/services/precompute_feed_service.rb b/app/services/precompute_feed_service.rb
index a591c90913..70a8e8afb1 100644
--- a/app/services/precompute_feed_service.rb
+++ b/app/services/precompute_feed_service.rb
@@ -21,3 +21,13 @@ class PrecomputeFeedService < BaseService
@skip_filled_timelines && FeedManager.instance.timeline_size(type, id) * 2 > FeedManager::MAX_ITEMS
end
end
+
+# app/services/precompute_feed_service.rb
+def call(user)
+ case user.feed_algorithm
+ when 'local_top'
+ LocalTopPostsService.new.call
+ else
+ Status.chronological # Default
+ end
+end
diff --git a/config/routes.rb b/config/routes.rb
index 1f97ddaaa4..bdfb8092d5 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -163,6 +163,17 @@ Rails.application.routes.draw do
resources :statuses, only: :show
end
+ # config/routes.rb
+ namespace :api do
+ namespace :v1 do
+ resources :timelines do
+ collection do
+ get :local_top
+ end
+ end
+ end
+end
+
resources :media, only: [:show] do
get :player
end
diff --git a/db/migrate/20240612000000_add_local_top_algorithm_support.rb b/db/migrate/20240612000000_add_local_top_algorithm_support.rb
new file mode 100644
index 0000000000..1eeec1eda4
--- /dev/null
+++ b/db/migrate/20240612000000_add_local_top_algorithm_support.rb
@@ -0,0 +1,12 @@
+# db/migrate/20240612000000_add_local_top_algorithm_support.rb
+class AddLocalTopAlgorithmSupport < ActiveRecord::Migration[6.1]
+ def change
+ # Ensure status_stats exists (Mastodon usually has this)
+ unless column_exists?(:statuses, :status_stat_id)
+ add_reference :statuses, :status_stat, foreign_key: { on_delete: :cascade }
+ end
+
+ # Add user preference for feed algorithm (default: chronological)
+ add_column :users, :feed_algorithm, :string, default: 'chronological'
+ end
+ end
\ No newline at end of file