public class Tuba.Views.Profile : Views.Accounts {
	const int TOTAL_STATIC_ITEMS = 3;

	public class NoteEditor : Adw.Dialog {
		public signal void note_done (string note);
		const int MAX_CHARS = 2000;
		GtkSource.View editor;
		Gtk.Label dialog_char_counter;
		Dialogs.Composer.Components.Editor.PlaceholderHack placeholder;

		public NoteEditor (string? note = "") {
			this.can_close = false;
			this.content_width = 400;
			this.content_height = 400;
			// translators: private note dialog title, shown when editing a private note on a profile
			this.title = _("Private Note");
			var toolbar_view = new Adw.ToolbarView ();
			var headerbar = new Adw.HeaderBar () {
				show_end_title_buttons = false,
				show_start_title_buttons = false
			};
			toolbar_view.add_top_bar (headerbar);

			var cancel_btn = new Gtk.Button.with_label (_("Cancel"));
			cancel_btn.clicked.connect (on_cancel);

			var save_btn = new Gtk.Button.with_label (_("Save")) {
				css_classes = {"suggested-action"}
			};
			save_btn.clicked.connect (on_save);
			headerbar.pack_start (cancel_btn);
			headerbar.pack_end (save_btn);

			editor = new GtkSource.View () {
				vexpand = true,
				hexpand = true,
				accepts_tab = false,
				wrap_mode = Gtk.WrapMode.WORD_CHAR,
				margin_bottom = 6,
				margin_top = 6,
				margin_start = 6,
				margin_end = 6,
				input_hints = WORD_COMPLETION | SPELLCHECK | EMOJI
			};
			editor.remove_css_class ("view");
			editor.add_css_class ("reset");
			this.focus_widget = editor;

			// translators: placeholder shown in the private note editor on profiles
			placeholder = new Dialogs.Composer.Components.Editor.PlaceholderHack (new Gtk.Label (_("Type your note…")) {
				valign = Gtk.Align.START,
				halign = Gtk.Widget.get_default_direction () == Gtk.TextDirection.RTL ? Gtk.Align.END : Gtk.Align.START,
				justify = Gtk.Justification.FILL,
				//  margin_top = 6,
				margin_start = 3,
				margin_end = 3,
				wrap = true,
				wrap_mode = Pango.WrapMode.WORD_CHAR,
				sensitive = false
			});
			editor.add_overlay (placeholder, 0, 0);
			editor.update_property (Gtk.AccessibleProperty.PLACEHOLDER, _("Type your note…"), -1);

			Adw.StyleManager.get_default ().notify["dark"].connect (update_style_scheme);
			update_style_scheme ();

			#if LIBSPELLING
				var adapter = new Spelling.TextBufferAdapter ((GtkSource.Buffer) editor.buffer, Spelling.Checker.get_default ());

				editor.extra_menu = adapter.get_menu_model ();
				editor.insert_action_group ("spelling", adapter);
				adapter.enabled = true;
			#endif

			toolbar_view.content = new Gtk.ScrolledWindow () {
				hexpand = true,
				vexpand = true,
				child = editor,
				propagate_natural_height = true,
				max_content_height = 209
			};

			dialog_char_counter = new Gtk.Label (MAX_CHARS.to_string ()) {
				tooltip_text = _("Remaining Characters"),
				css_classes = { "numeric", "dimmed" },
				margin_end = 6
			};
			headerbar.pack_end (dialog_char_counter);
			editor.buffer.changed.connect (on_editor_buffer_change);
			if (note != null && note != "") editor.buffer.text = note;
			this.child = toolbar_view;
		}

		private void on_editor_buffer_change () {
			int total_count = Utils.Counting.chars (editor.buffer.text, "en");
			placeholder.visible = total_count == 0;

			dialog_char_counter.label = (MAX_CHARS - total_count).to_string ();
			if (total_count < MAX_CHARS) {
				dialog_char_counter.remove_css_class ("error");
			} else {
				dialog_char_counter.add_css_class ("error");
			}
		}

		private void update_style_scheme () {
			var manager = GtkSource.StyleSchemeManager.get_default ();
			string scheme_name = "Adwaita";
			if (Adw.StyleManager.get_default ().dark) scheme_name += "-dark";
			((GtkSource.Buffer) editor.buffer).style_scheme = manager.get_scheme (scheme_name);
		}

		private void on_save () {
			note_done (editor.buffer.text);
			this.force_close ();
		}

		private void on_cancel () {
			this.force_close ();
		}
	}

	public class ProfileAccount : Widgetizable, GLib.Object {
		public API.Account account { get; construct set; }
		public API.Relationship rs { get; construct set; }

		public ProfileAccount (API.Account t_acc) {
			Object (account: t_acc, rs: new API.Relationship.for_account (t_acc));
		}

		public async bool update_profile () {
			RequestV2 req = new RequestV2 (@"/api/v1/accounts/$(account.id)") { account = accounts.active };

			try {
				var in_stream = yield req.exec (null);
				Json.Parser parser = yield Network.get_parser_from_inputstream_async (in_stream);
				var node = network.parse_node (parser);
				var updated = API.Account.from (node);

				account.display_name = updated.display_name;
				account.note = updated.note;
				account.locked = updated.locked;
				account.header = updated.header;
				account.header_description = updated.header_description;
				account.avatar = updated.avatar;
				account.avatar_description = updated.avatar_description;
				account.bot = updated.bot;
				account.emojis = updated.emojis;
				account.followers_count = updated.followers_count;
				account.following_count = updated.following_count;
				account.statuses_count = updated.statuses_count;
				account.fields = updated.fields;
				account.moved = updated.moved;

				return true;
			} catch (Error e) {
				warning (@"Couldn't update account $(account.id): $(e.message)");
				app.toast (e.message);
			}

			return false;
		}

		public override Gtk.Widget to_widget () {
			return new Widgets.Cover (this);
		}

		public Gtk.Widget to_mini_widget () {
			return new Widgets.Cover (this, true);
		}
	}

	public class FilterGroup : Widgetizable, GLib.Object {
		public bool visible { get; set; default=true; }
		public bool show_media { get; set; default=true; }
		public bool show_featured { get; set; default=true; }

		public override Gtk.Widget to_widget () {
			var widget = new Widgets.ProfileFilterGroup (show_media, show_featured);
			this.bind_property ("visible", widget, "visible", GLib.BindingFlags.SYNC_CREATE);
			return widget;
		}
	}

	public class ErrorMessageRow : Widgetizable, GLib.Object {
		public bool visible { get; set; default=true; }
		public string message { get; set; default = ""; }

		public override Gtk.Widget to_widget () {
			var widget = new Gtk.Label (message) {
				wrap = true,
				wrap_mode = WORD_CHAR,
				css_classes = { "title-1" },
				margin_start = 16,
				margin_end = 16,
				margin_top = 16,
				margin_bottom = 16,
				justify = CENTER
			};

			var row = new Gtk.ListBoxRow () {
				child = widget,
				overflow = Gtk.Overflow.HIDDEN,
				activatable = false
			};

			this.bind_property ("visible", row, "visible", GLib.BindingFlags.SYNC_CREATE);
			this.bind_property ("message", widget, "label", GLib.BindingFlags.SYNC_CREATE);
			return row;
		}
	}

	public override bool empty {
		get { return false; }
	}

	public ProfileAccount profile { get; construct set; }
	public Widgets.ProfileFilterGroup.Filter filter { get; set; default = Widgets.ProfileFilterGroup.Filter.POSTS; }
	public string source { get; set; default = "statuses"; }
	private signal void cover_profile_update (API.Account acc);

	protected Gtk.MenuButton menu_button;
	protected SimpleAction muting_action;
	protected SimpleAction hiding_reblogs_action;
	protected SimpleAction blocking_action;
	protected SimpleAction domain_blocking_action;
	protected SimpleAction endorse_action;
	protected SimpleAction ar_list_action;
	protected SimpleAction add_note_action;
	protected SimpleAction notify_on_new_post_action;
	//  protected SimpleAction source_action;

	private FilterGroup filter_group;
	private ErrorMessageRow error_message_row = new ErrorMessageRow () {
		visible = false
	};
	public Profile (API.Account acc) {
		Object (
			profile: new ProfileAccount (acc),
			label: _("Profile"),
			allow_nesting: true,
			url: @"/api/v1/accounts/$(acc.id)/statuses",
			tuba_row: true
		);

		this.bind_property ("empty-state-title", error_message_row, "message", SYNC_CREATE);
		filter_group = new FilterGroup () {
			show_featured = acc.show_featured,
			show_media = acc.show_media
		};
		model.insert (0, profile);
		model.insert (1, filter_group);
		model.insert (2, error_message_row);
		profile.rs.invalidated.connect (on_rs_updated);

		if (acc.is_self ()) {
			update_profile_cover ();
			app.refresh_featured.connect (on_featured_refresh_request);
			app.remove_status_widget.connect (remove_own_status_widget);
		}
	}
	~Profile () {
		debug ("Destroying Profile view");
	}

	private void remove_own_status_widget (string status_id) {
		for (uint i = 0; i < model.get_n_items (); i++) {
			var status_obj = model.get_item (i) as API.Status;
			if (status_obj != null && (status_obj.id == status_id || status_obj.formal.id == status_id)) {
				safely_remove ((int) i);
				if (status_obj.id == status_id)
					break;
			}
		}
	}

	private void on_featured_refresh_request () {
		if (this.filter == FEATURED && this.get_mapped ()) on_refresh ();
	}

	public async void append_pinned () {
		if (source == "statuses" && filter == Widgets.ProfileFilterGroup.Filter.POSTS) {
			var req = new RequestV2 (@"/api/v1/accounts/$(profile.account.id)/statuses") { account = account, ctx = this };
			req.add_parameter ("pinned", "true");
			try {
				var in_stream = yield req.exec (null);
				Json.Parser parser = yield Network.get_parser_from_inputstream_async (in_stream);
				Object[] to_add = {};
				Network.parse_array (parser, node => {
					var e = Tuba.Helper.Entity.from_json (node, typeof (API.Status));
					var e_status = e as API.Status;
					if (e_status != null) e_status.pinned = true;

					to_add += e_status;
				});
				model.splice (TOTAL_STATIC_ITEMS, 0, to_add);
			} catch (Error e) {
				warning (@"Couldn't append pinned for $(profile.account.id): $(e.code) $(e.message)");
			}
		}
	}

	public override void on_request_finish () {
		base.on_request_finish ();
		on_content_changed ();
	}

	private void on_cover_aria_update (Widgets.Cover p_cover, string new_aria) {
		var lbr = p_cover.get_parent ();
		if (lbr != null) {
			lbr.update_property (Gtk.AccessibleProperty.LABEL, new_aria, -1);
			lbr.update_relation (Gtk.AccessibleRelation.DESCRIBED_BY, p_cover.note, null, -1);
		}
	}

	public override Gtk.Widget on_create_model_widget (Object obj) {
		var widget = base.on_create_model_widget (obj);

		var widget_cover = widget as Widgets.Cover;
		if (widget_cover != null) {
			widget_cover.rs_invalidated.connect (on_rs_updated);
			widget_cover.timeline_change.connect (change_timeline_source);
			widget_cover.aria_updated.connect (on_cover_aria_update);
			widget_cover.edit_note.connect (on_edit_cover_note);
			widget_cover.remove_css_class ("card");
			widget_cover.remove_css_class ("card-spacing");
			this.cover_profile_update.connect (widget_cover.update_cover_from_profile);

			var row = new Gtk.ListBoxRow () {
				focusable = true,
				activatable = false,
				child = widget_cover,
				css_classes = { "card-spacing", "card", "ttl-profile-cover-container" },
				overflow = Gtk.Overflow.HIDDEN
			};
			widget_cover.update_aria ();

			return row;
		}

		var widget_status = widget as Widgets.Status;
		if (widget_status != null && profile.account.id == accounts.active.id) {
			widget_status.show_toggle_pinned_action ();
			widget_status.pin_changed.connect (on_refresh);
		}

		var widget_filter_group = widget as Widgets.ProfileFilterGroup;
		if (widget_filter_group != null) {
			widget_filter_group.remove_css_class ("card");
			widget_filter_group.remove_css_class ("card-spacing");
			widget_filter_group.filter_change.connect (change_filter);
		}

		return widget;
	}

	#if USE_LISTVIEW
		protected override void bind_listitem_cb (GLib.Object item) {
			base.bind_listitem_cb (item);

			if (((((Gtk.ListItem) item).item) as ProfileAccount) != null)
				((Gtk.ListItem) item).activatable = false;
		}
	#endif

	public override void on_refresh () {
		error_message_row.visible = false;
		base.on_refresh ();
		append_pinned.begin ();
		append_featured_tags.begin ();
	}

	public override void on_manual_refresh () {
		update_profile_cover ();
		base.on_manual_refresh ();
	}

	protected void change_timeline_source (string t_source) {
		if (t_source == "statuses-like") {
			source = this.filter == FEATURED ? "endorsements" : "statuses";
		} else {
			source = t_source;
		}
		string pleroma =
			source == "endorsements"
			&& accounts.active.instance_info != null
			&& accounts.active.instance_info.pleroma != null
			&& !(InstanceAccount.InstanceFeatures.ICESHRIMP in accounts.active.tuba_instance_features)
			? "/pleroma" : "";

		filter_group.visible = source == "statuses" || source == "endorsements";
		source_meta_update (source);

		url = @"/api/v1$pleroma/accounts/$(profile.account.id)/$source";
		invalidate_actions (true);
	}

	private void source_meta_update (string t_source) {
		switch (t_source) {
			case "statuses":
				accepts = typeof (API.Status);
				// translators: posts tab on profiles, shown when empty.
				empty_state_title = _("No Posts");
				break;
			case "followers":
				accepts = typeof (API.Account);
				empty_state_title = profile.account.followers_count > 0
				// translators: profile tab, when information (followers/following lists) is hidden.
				? _("This user has chosen to not make this information available")
				// translators: followers tab on profiles, shown when empty.
				: _("No Followers");
				break;
			case "following":
				accepts = typeof (API.Account);
				empty_state_title = profile.account.following_count > 0
				? _("This user has chosen to not make this information available")
				// translators: following tab on profiles, shown when empty or hidden.
				: _("This user doesn't follow anyone yet");
				break;
			case "endorsements":
				accepts = typeof (API.Account);
				// translators: featured tab on profiles, shown when empty.
				empty_state_title = _("This user doesn't have any featured hashtags or accounts.");
				break;
			default:
				assert_not_reached ();
		}
	}

	protected void change_filter (Widgets.ProfileFilterGroup.Filter filter) {
		bool was_featured = this.filter == FEATURED;
		this.filter = filter;
		if (this.filter == FEATURED || was_featured) {
			change_timeline_source ("statuses-like");
		} else {
			source_meta_update (source);
			invalidate_actions (true);
		}
	}

	private async void append_featured_tags () {
		if (source == "endorsements" && filter == Widgets.ProfileFilterGroup.Filter.FEATURED) {
			try {
				Object[] to_add = {};

				var req = new RequestV2 (@"/api/v1/accounts/$(profile.account.id)/featured_tags") { account = account, ctx = this };
				var in_stream = yield req.exec (null);

				Json.Parser parser = yield Network.get_parser_from_inputstream_async (in_stream);
				Network.parse_array (parser, node => {
					to_add += Tuba.Helper.Entity.from_json (node, typeof (API.FeaturedTag));
				});

				//  req = new Request.GET (@"/api/v1/accounts/$(profile.account.id)/endorsements")
				//  		.with_account (account)
				//  		.with_ctx (this);
				//  yield req.await ();

				//  parser = Network.get_parser_from_inputstream (req.response_body);
				//  Network.parse_array (parser, node => {
				//  	to_add += Tuba.Helper.Entity.from_json (node, typeof (API.Account));
				//  });
				model.splice (TOTAL_STATIC_ITEMS, 0, to_add);
			} catch (Error e) {
				on_error (e.code, e.message);
			}
		}
	}


	public override void on_content_changed () {
		error_message_row.visible = false;
		base.on_content_changed ();
		if (has_finished_request && base_status == null && model.get_n_items () == TOTAL_STATIC_ITEMS) {
			error_message_row.visible = true;
		}
	}

	protected override void build_header () {
		base.build_header ();

		menu_button = new Gtk.MenuButton () {
			icon_name = "view-more-symbolic"
		};
		var menu_builder = new Gtk.Builder.from_resource (@"$(Build.RESOURCES)ui/menus.ui");
		var menu = "profile-menu";
		menu_button.menu_model = menu_builder.get_object (menu) as MenuModel;
		menu_button.popover.width_request = 250;
		header.pack_end (menu_button);

		if (profile.account.is_self ()) {
			var edit_btn = new Gtk.Button.from_icon_name ("document-edit-symbolic") {
				//  translators: verb, title or button that when clicked will show the profile editor
				tooltip_text = _("Edit Profile")
			};
			edit_btn.clicked.connect (open_edit_page);
			header.pack_end (edit_btn);
		}

		//  rs_button = new Widgets.RelationshipButton () {
		//  	rs = this.rs
		//  };
	}

	private void open_edit_page () {
		var dialog = new Dialogs.ProfileEdit (profile.account.is_self () ? accounts.active : profile.account);
		dialog.saved.connect (on_edit_save);
		dialog.present (app.main_window);
	}

	private void on_edit_save () {
		if (profile.account.is_self ()) {
			//  for (uint i = 0; i < model.get_n_items (); i++) {
			//  	var status_obj = (API.Status)model.get_item (i);
			//  	if (status_obj.formal.account.id == profile.account.id) {
			//  		Tuba.EntityCache.remove (status_obj.formal.uri);
			//  	}
			//  }

			this.cover_profile_update (accounts.active);
		}
	}

	private void update_profile_cover () {
		profile.update_profile.begin ((obj, res) => {
			if (profile.update_profile.end (res)) {
				this.cover_profile_update (profile.account);
			}
		});
	}

	protected override void clear () {
		base.clear_all_but_first (TOTAL_STATIC_ITEMS);
	}

	private async void on_domain_block_real (bool block) {
		// translators: confirmation dialog on blocking/unblocking an instance; variable is a string instance
		var q = block ? _("Block Entire \"%s\"?") : _("Unblock Entire \"%s\"?");
		var question = yield app.question (
			{q.printf (profile.account.domain), false},
			// translators: confirmation dialog explaining what blocking an instance will do
			{_("Blocking a domain will:\n\n• Remove its public posts and notifications from your timelines\n• Remove its followers from your account\n• Prevent you from following its users"), false},

			app.main_window,
			{ { block ? _("Block") : _("Unblock"), Adw.ResponseAppearance.DESTRUCTIVE }, { _("Cancel"), Adw.ResponseAppearance.DEFAULT } },
			null,
			false
		);

		if (question.truthy ()) {
			var req = new RequestV2 ("/api/v1/domain_blocks", block ? RequestV2.Method.POST : RequestV2.Method.DELETE) { account = accounts.active };
			req.add_parameter ("domain", profile.account.domain);
			try {
				yield req.exec (null);
				profile.rs.request ();
			} catch (Error e) {
				warning (@"Couldn't block domain $(profile.account.domain): $(e.code) $(e.message)");
				// translators: error toast, variable is a string error message
				app.toast (_("Couldn't block domain: %s").printf (e.message));
			}
		}
	}

	protected override void build_actions () {
		base.build_actions ();

		notify_on_new_post_action = new SimpleAction.stateful ("notify_on_post", null, false);
		notify_on_new_post_action.change_state.connect (v => {
			profile.rs.modify ("follow", {{"notify", v.get_boolean ().to_string ()}});
			invalidate_actions (false);
		});
		actions.add_action (notify_on_new_post_action);

		//  source_action = new SimpleAction.stateful ("source", VariantType.STRING, source);
		//  source_action.change_state.connect (v => {
		//  	source = v.get_string ();
		//  	source_action.set_state (source);
		//  	accepts = (source == "statuses" ? typeof (API.Status) : typeof (API.Account));

		//  	url = @"/api/v1/accounts/$(profile.account.id)/$source";
		//  	invalidate_actions (true);
		//  });
		//  actions.add_action (source_action);
		ar_list_action = new SimpleAction ("ar_list", null);
		ar_list_action.activate.connect (v => {
			var dpl = new Dialogs.ProfileLists (profile.account.id, profile.account.handle);
			dpl.errored.connect (on_error);
			dpl.present (app.main_window);
		});
		actions.add_action (ar_list_action);

		var mention_action = new SimpleAction ("mention", VariantType.STRING);
		mention_action.activate.connect (v => {
			new Dialogs.Composer.Dialog ({@"$(profile.account.handle) ", null, null, null, null, null, null, false, false}, v.get_string ());
		});
		actions.add_action (mention_action);

		var collection_action = new SimpleAction ("collections", null);
		collection_action.activate.connect (v => {
			app.main_window.open_view (new Views.CollectionList (profile.account.id));
		});
		actions.add_action (collection_action);
		collection_action.set_enabled (accounts.active.tuba_api_versions.mastodon >= 10 && !profile.account.hide_collections);

		add_note_action = new SimpleAction ("add-note", null);
		add_note_action.activate.connect (v => {
			on_edit_note ();
		});
		actions.add_action (add_note_action);

		var copy_handle_action = new SimpleAction ("copy_handle", null);
		copy_handle_action.activate.connect (v => {
			Utils.Host.copy (profile.account.full_handle);
			app.toast (_("Copied handle to clipboard"));
		});
		actions.add_action (copy_handle_action);

		var open_in_browser_action = new SimpleAction ("open_in_browser", null);
		open_in_browser_action.activate.connect (v => {
			#if WEBKIT
				if (settings.use_in_app_browser_if_available && Views.Browser.can_handle_url (profile.account.url)) {
					(new Views.Browser.with_url (profile.account.url)).present (app.main_window);
					return;
				}
			#endif

			Utils.Host.open_url.begin (profile.account.url);
		});
		actions.add_action (open_in_browser_action);

		var report_action = new SimpleAction ("report", null);
		report_action.activate.connect (v => {
			new Dialogs.Report (profile.account);
		});
		actions.add_action (report_action);

		muting_action = new SimpleAction.stateful ("muting", null, false);
		muting_action.change_state.connect (v => {
			var state = v.get_boolean ();
			if (state) {
				profile.rs.question_modify_mute (profile.account.handle);
			} else {
				profile.rs.modify ("unmute");
			}
		});
		actions.add_action (muting_action);

		hiding_reblogs_action = new SimpleAction.stateful ("hiding_reblogs", null, false);
		hiding_reblogs_action.change_state.connect (v => {
			if (!profile.rs.following) {
				warning ("Trying to hide boosts while not following an account.");
				return;
			}

			var state = !v.get_boolean ();
			profile.rs.modify ("follow", {{"reblogs", state.to_string ()}});
		});
		actions.add_action (hiding_reblogs_action);

		blocking_action = new SimpleAction.stateful ("blocking", null, false);
		blocking_action.change_state.connect (v => {
			var block = v.get_boolean ();
			profile.rs.question_modify_block (profile.account.handle, block);
		});
		actions.add_action (blocking_action);

		domain_blocking_action = new SimpleAction.stateful ("domain_blocking", null, false);
		domain_blocking_action.change_state.connect (v => {
			var block = v.get_boolean ();
			on_domain_block_real.begin (block);
		});
		actions.add_action (domain_blocking_action);

		string endorse_str = accounts.active.tuba_api_versions.mastodon >= 6 ? "endorse" : "pin";
		string unendorse_str = accounts.active.tuba_api_versions.mastodon >= 6 ? "unendorse" : "unpin";

		endorse_action = new SimpleAction.stateful ("endorsed", null, false);
		endorse_action.change_state.connect (v => {
			profile.rs.modify (v.get_boolean () ? endorse_str : unendorse_str);
			invalidate_actions (false);
		});
		actions.add_action (endorse_action);

		invalidate_actions (false);
	}

	void invalidate_actions (bool refresh) {
		muting_action.set_state (profile.rs.muting);
		endorse_action.set_state (profile.rs.endorsed);
		endorse_action.set_enabled (profile.rs.following);
		hiding_reblogs_action.set_state (!profile.rs.showing_reblogs);
		hiding_reblogs_action.set_enabled (profile.rs.following);
		blocking_action.set_state (profile.rs.blocking);
		domain_blocking_action.set_state (profile.rs.domain_blocking);
		domain_blocking_action.set_enabled (accounts.active.domain != profile.account.domain);
		ar_list_action.set_enabled (profile.account.id != accounts.active.id && profile.rs.following);
		notify_on_new_post_action.set_enabled (profile.account.id != accounts.active.id && profile.rs.following);
		notify_on_new_post_action.set_state (profile.rs.notifying);
		add_note_action.set_enabled (profile.account.id != accounts.active.id);

		if (refresh) {
			page_next = null;
			on_refresh ();
		}
	}

	void on_rs_updated () {
		invalidate_actions (false);
	}

	//  public override Request append_params (Request req) {
	//  	if (page_next == null && source == "statuses") {
	//  		switch (this.filter) {
	//  			case Widgets.ProfileFilterGroup.Filter.POSTS:
	//  				req.with_param ("exclude_replies", "true");
	//  				break;
	//  			case Widgets.ProfileFilterGroup.Filter.REPLIES:
	//  				req.with_param ("exclude_replies", "false");
	//  				req.with_param ("exclude_reblogs", "true");
	//  				break;
	//  			case Widgets.ProfileFilterGroup.Filter.MEDIA:
	//  				req.with_param ("only_media", "true");
	//  				break;
	//  			case Widgets.ProfileFilterGroup.Filter.FEATURED: break;
	//  			default:
	//  				assert_not_reached ();
	//  		}
	//  	}
	//  	return base.append_params (req);
	//  }

	public override void append_params_v2 (RequestV2 req) {
		if (page_next == null && source == "statuses") {
			switch (this.filter) {
				case Widgets.ProfileFilterGroup.Filter.POSTS:
					req.add_parameter ("exclude_replies", "true");
					break;
				case Widgets.ProfileFilterGroup.Filter.REPLIES:
					req.add_parameter ("exclude_replies", "false");
					req.add_parameter ("exclude_reblogs", "true");
					break;
				case Widgets.ProfileFilterGroup.Filter.MEDIA:
					req.add_parameter ("only_media", "true");
					if (!profile.account.show_media_replies)
						req.add_parameter ("exclude_replies", "true");
					break;
				case Widgets.ProfileFilterGroup.Filter.FEATURED: break;
				default:
					assert_not_reached ();
			}
		}
		base.append_params_v2 (req);
	}

	private void on_edit_cover_note () {
		on_edit_note ();
	}

	private void on_edit_note (string? note = null) {
		var note_editor = new NoteEditor (note == null ? profile.rs.note : note);
		note_editor.note_done.connect (on_note_done);
		note_editor.present (app.main_window);
	}

	private void on_note_done (string new_note) {
		on_note_done_real.begin (new_note);
	}

	private async void on_note_done_real (string new_note) {
		try {
			yield profile.rs.modify_note_real (new_note);
		} catch (Error e) {
			warning (@"Error while writing note: $(e.code) $(e.message)");
			app.toast ("%s: %s".printf (_("Error"), e.message));
			on_edit_note (new_note);
		}
	}
}
