Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick reply from board index #799

Open
chanadmins opened this issue Aug 19, 2024 · 19 comments
Open

Quick reply from board index #799

chanadmins opened this issue Aug 19, 2024 · 19 comments

Comments

@chanadmins
Copy link

I think this should be available as a basic feature. Forks like bazukachan or kissu already have that. Yotsuba, Mitsuba, Lynxchan, JSchan, Willboard, basically almost every imageboard software besides vichan has option to post in thread from quick reply without opening it. Would love to see this added.
example on magalichan:
tst

@perdedora
Copy link
Member

I can open PR with my changes

@perdedora
Copy link
Member

Done

@chanadmins
Copy link
Author

Done

I added your changes and quick reply loads from index but I can't post anything from it: "Your request looks automated; Post discarded."

@perdedora
Copy link
Member

perdedora commented Aug 28, 2024

This is likely happening due to a mismatch between submit button values. The variables in main.js is to fix this.

Edit:
Oh shit, I forgot to close the tag.

@perdedora
Copy link
Member

Can you try again with this change?
60e2ca2

@chanadmins
Copy link
Author

Can you try again with this change? 60e2ca2

Still the same
sc

I think it's because of that whole antibot thing in functions.php. I remember I implemented quick reply from index once and it involved removing the antibot block from functions.php and some other stuff. It creates non-existing fields or something so it prevents posting from index quick reply.

@perdedora
Copy link
Member

Weird. this is literally the code running on magali with antibot on.
Going to take a closer look later

@perdedora
Copy link
Member

Okay, so the problem was: the hash sent to checkSpam was the hash of the index, not the thread. Which failed everytime because the extra_salt was wrong.

This is the best solution I could come up: 4b20ad5

@chanadmins
Copy link
Author

Okay, so the problem was: the hash sent to checkSpam was the hash of the index, not the thread. Which failed everytime because the extra_salt was wrong.

This is the best solution I could come up: 4b20ad5

It works now, thanks!

@chanadmins
Copy link
Author

chanadmins commented Aug 30, 2024

Wait, shouldn't quick reply change threads based on the post number I click?
c
So here I click >>6038 and quick reply appears to thread Quick Reply (5963)
but then I click >>6100 and quick reply should change to Quick Reply (6074) but it stays Quick Reply (5963)
At least that's how most of the implementations I've seen work (like on Willboard), with dynamic changing threads.

@perdedora
Copy link
Member

Wait, shouldn't quick reply change threads based on the post number I click?

In the perfect javascript, yes. We can force to "change" by closing and opening again.

	$(window).on('cite', function(e, id, with_link) {
		if ($(this).width() <= 400)
			return;
		if ($('#quick-reply').length) $('#quick-reply').remove();
		show_quick_reply(id);
		if (with_link) {
			$(document).ready(function() {
				if ($('#' + id).length) {
					highlightReply(id);
					$(document).scrollTop($('#' + id).offset().top);
				}

				// Honestly, I'm not sure why we need setTimeout() here, but it seems to work.
				// Same for the "tmp" variable stuff you see inside here:
				setTimeout(function() {
					var tmp = $('#quick-reply textarea[name="body"]').val();
					$('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
				}, 1);
			});
		}
	});

@chanadmins
Copy link
Author

chanadmins commented Aug 30, 2024

Wait, shouldn't quick reply change threads based on the post number I click?

In the perfect javascript, yes. We can force to "change" by closing and opening again.

Any way to do this without opening/closing quick reply window and just clicking post number?
I remember it worked in my previous implementation of this

/*
 * quick-reply.js
 * https://github.com/savetheinternet/Tinyboard/blob/master/js/quick-reply.js
 *
 * Released under the MIT license
 * Copyright (c) 2013 Michael Save <[email protected]>
 * Copyright (c) 2013-2014 Marcin Łabanowski <[email protected]>
 *
 * Usage:
 *   $config['additional_javascript'][] = 'js/jquery.min.js';
 *   $config['additional_javascript'][] = 'js/jquery-ui.custom.min.js'; // Optional; if you want the form to be draggable.
 *   $config['additional_javascript'][] = 'js/quick-reply.js';
 *
 */

(function() {
	var new_reply = "Odpowiedz"; // ta zmienna musi być taka sama jak przycisk nowej odpowiedzi w temacie

    if(document.querySelector("body").classList.contains("active-index")) {
        document.addEventListener("click", function(event) {
            if(event.target.classList.contains("post_no") && event.target.id === "") {
                event.preventDefault();
            }
        });
    }

	var settings = new script_settings('quick-reply');

	var do_css = function() {
		$('#quick-reply-css').remove();

		// Find background of reply posts
		var dummy_reply = $('<div class="post reply"></div>').appendTo($('body'));
		var reply_background = dummy_reply.css('backgroundColor');
		var reply_border_style = dummy_reply.css('borderStyle');
		var reply_border_color = dummy_reply.css('borderColor');
		var reply_border_width = dummy_reply.css('borderWidth');
		dummy_reply.remove();

		$('<style type="text/css" id="quick-reply-css">\
		#quick-reply {\
			position: fixed;\
			right: 5%;\
			top: 5%;\
			float: right;\
			display: block;\
			padding: 0 0 0 0;\
			width: 300px;\
			z-index: 100;\
		}\
		#quick-reply table {\
			border-collapse: collapse;\
			background: ' + reply_background + ';\
			border-style: ' + reply_border_style + ';\
			border-width: ' + reply_border_width + ';\
			border-color: ' + reply_border_color + ';\
			margin: 0;\
			width: 100%;\
		}\
		#quick-reply tr td:nth-child(2) {\
			white-space: nowrap;\
			text-align: right;\
			padding-right: 4px;\
		}\
		#quick-reply tr td:nth-child(2) input[type="submit"] {\
			width: 100%;\
		}\
		#quick-reply th, #quick-reply td {\
			margin: 0;\
			padding: 0;\
		}\
		#quick-reply th {\
			text-align: center;\
			padding: 2px 0;\
			border: 1px solid #222;\
		}\
		#quick-reply th .handle {\
			float: left;\
			width: 100%;\
			display: inline-block;\
		}\
		#quick-reply th .close-btn {\
			float: right;\
			padding: 0 5px;\
		}\
		#quick-reply input[type="text"], #quick-reply select {\
			width: 100%;\
			padding: 2px;\
			font-size: 10pt;\
			box-sizing: border-box;\
			-webkit-box-sizing:border-box;\
			-moz-box-sizing: border-box;\
		}\
		#quick-reply textarea {\
			/* width: 100%; */\
			min-width: 100%;\
			box-sizing: border-box;\
			-webkit-box-sizing:border-box;\
			-moz-box-sizing: border-box;\
			font-size: 10pt;\
			resize: vertical horizontal;\
		}\
		#quick-reply input, #quick-reply select, #quick-reply textarea {\
			margin: 0 0 1px 0;\
		}\
		#quick-reply input[type="file"] {\
			padding: 5px 2px;\
		}\
		#quick-reply .nonsense {\
			display: none;\
		}\
		#quick-reply td.submit {\
			width: 1%;\
		}\
		#quick-reply td.recaptcha {\
			text-align: center;\
			padding: 0 0 1px 0;\
		}\
		#quick-reply td.recaptcha span {\
			display: inline-block;\
			width: 100%;\
			background: white;\
			border: 1px solid #ccc;\
			cursor: pointer;\
		}\
		#quick-reply td.recaptcha-response {\
			padding: 0 0 1px 0;\
		}\
		@media screen and (max-width: 400px) {\
			#quick-reply {\
				/* display: none !important; */\
			}\
		}\
		</style>').appendTo($('head'));
	};

	var show_quick_reply = function(target_id){

		if($('div.banner').length == 0){
				var thread_id = $("#reply_" + target_id + ",#op_"+target_id).parent().attr("id").split("_")[1];
				var in_index = true;
		}
		else
			var in_index = false;

		if($('#quick-reply').length != 0) {
			return;
		}

		do_css();

		var $postForm = $('form[name="post"]').clone();

		$postForm.clone();
		$dummyStuff = $('<div class="nonsense"></div>').appendTo($postForm);

		$postForm.find('table tr').each(function() {
			var $th = $(this).children('th:first');
			var $td = $(this).children('td:first');
			if ($th.length && $td.length) {
				$td.attr('colspan', 2);

				if ($td.find('input[type="text"]').length) {
					// Replace <th> with input placeholders
					$td.find('input[type="text"]')
						.removeAttr('size')
						.attr('placeholder', $th.clone().children().remove().end().text());
				}

				// Move anti-spam nonsense and remove <th>
				$th.contents().filter(function() {
					return this.nodeType == 3; // Node.TEXT_NODE
				}).remove();
				$th.contents().appendTo($dummyStuff);
				$th.remove();

				if ($td.find('input[name="password"]').length) {
					// Hide password field
					$(this).hide();
				}

				// Fix submit button
				if ($td.find('input[type="submit"]').length) {
					$td.removeAttr('colspan');
					$('<td class="submit"></td>').append($td.find('input[type="submit"]')).insertAfter($td);
				}

				// reCAPTCHA
				if ($td.find('#recaptcha_widget_div').length) {
					// Just show the image, and have it interact with the real form.
					var $captchaimg = $td.find('#recaptcha_image img');

					$captchaimg
						.removeAttr('id')
						.removeAttr('style')
						.addClass('recaptcha_image')
						.click(function() {
							$('#recaptcha_reload').click();
						});

					// When we get a new captcha...
					$('#recaptcha_response_field').focus(function() {
						if ($captchaimg.attr('src') != $('#recaptcha_image img').attr('src')) {
							$captchaimg.attr('src', $('#recaptcha_image img').attr('src'));
							$postForm.find('input[name="recaptcha_challenge_field"]').val($('#recaptcha_challenge_field').val());
							$postForm.find('input[name="recaptcha_response_field"]').val('').focus();
						}
					});

					$postForm.submit(function() {
						setTimeout(function() {
							$('#recaptcha_reload').click();
						}, 200);
					});

					// Make a new row for the response text
					var $newRow = $('<tr><td class="recaptcha-response" colspan="2"></td></tr>');
					$newRow.children().first().append(
						$td.find('input').removeAttr('style')
					);
					$newRow.find('#recaptcha_response_field')
						.removeAttr('id')
						.addClass('recaptcha_response_field')
						.attr('placeholder', $('#recaptcha_response_field').attr('placeholder'));

					$('#recaptcha_response_field').addClass('recaptcha_response_field')

					$td.replaceWith($('<td class="recaptcha" colspan="2"></td>').append($('<span></span>').append($captchaimg)));

					$newRow.insertAfter(this);
				}

				// Upload section
				if ($td.find('input[type="file"]').length) {
					if($td.find('input[name="file"]').length){
						$file = $td.find('input[name="file"]');
						$file.attr("style","width:90%");
					}

					//captcha stuff
					var $captcha_span = $td.find('span[name="captchasel"]');
					var $caprow = $('<tr><td colspan="2"></td></tr>');
					$captcha_span.clone().appendTo($caprow.find('td'));
					$caprow.insertBefore(this);

					if ($td.find('input[name="file_url"]').length) {
						$file_url = $td.find('input[name="file_url"]');

						//if (settings.get('show_remote', false)) {
							// Make a new row for it
							var $newRow = $('<tr><td colspan="2"></td></tr>');

							$file_url.clone().attr('placeholder', _('\
			    Upload URL')).appendTo($newRow.find('td'));

							$newRow.insertAfter(this);
						//}
						$file_url.parent().remove();


						$td.find('label').remove();
						$td.contents().filter(function() {
							return this.nodeType == 3; // Node.TEXT_NODE
						}).remove();
						$td.find('input[name="file_url"]').removeAttr('id');
					}

					if ($(this).find('input[name="spoiler"]').length) {
						$td.removeAttr('colspan');
					}
				}


				//captcha controls
				if($td.find('input[name=captype]').length > 0){

				}

				// Disable embedding if configured so
				// if (!settings.get('show_embed', false) && $td.find('input[name="embed"]').length) {
					// $(this).remove();
				// }

				// Remove oekaki if existent
				if ($(this).is('#oekaki')) {
					$(this).remove();
				}

				// Remove upload selection
				if ($td.is('#upload_selection')) {
					$(this).remove();
				}

				// Remove mod controls, because it looks shit.
				if ($td.find('input[type="checkbox"]').length) {
					var tr = this;
					$td.find('input[type="checkbox"]').each(function() {
						if ($(this).attr('name') == 'spoiler') {
							$td.find('label').remove();
							$(this).attr('id', 'q-spoiler-image');
							$postForm.find('input[type="file"]').parent()
								.removeAttr('colspan')
								.after($('<td class="spoiler"></td>').append(this, ' ', $('<label for="q-spoiler-image">').text(_('Spoiler Image'))));
						} else if ($(this).attr('name') == 'no_country') {
							$td.find('label,input[type="checkbox"]').remove();
						} else {
							$(tr).remove();
						}
					});
				}

				$td.find('small').hide();
			}
			if($td.find('[name="markup-hint"]').length){
				$td.remove();
			}
		});

		$postForm.find('textarea[name="body"]').removeAttr('id').removeAttr('cols').attr('placeholder', _('Comment'));

		$postForm.find('textarea:not([name="body"]),input[type="hidden"]:not(.captcha_cookie)').removeAttr('id').appendTo($dummyStuff);

		$postForm.find('br').remove();
		$postForm.find('table').prepend('<tr><th colspan="2">\
			<span class="handle">\
				<a class="close-btn" href="javascript:void(0)">×</a>\
				' + _('Quick Reply') + '\
			</span>\
			</th></tr>');

		$postForm.attr('id', 'quick-reply');

		$postForm.appendTo($('body')).hide();
		$origPostForm = $('form[name="post"]:first');


		// insert the thread that this post will be placed in and modify fields
		if(in_index) {
			// ziemniachan fix
			$("#quick-reply").find("input[type=submit]").remove();
			document.querySelectorAll("#quick-reply tr")[2].remove();
			document.querySelector("#quick-reply [name=email]")?.parentNode.setAttribute("colspan", "1");
			document.querySelectorAll("#quick-reply tr")[1].innerHTML += '<td class="submit"><input accesskey="s" style="margin-left:2px;" type="submit" name="post" value="'+new_reply+'"></td>';

			$("#quick-reply textarea").attr("id", "index-body");
			$("<input type='hidden' name='thread' value='" + thread_id + "'>").appendTo($("#quick-reply"));
			$("#quick-reply .handle").append(document.createTextNode("(>>" + thread_id + ")"));
		}
			//$("#quick-reply input[type=submit]").attr("value", "New Reply");
		// }
		// otherwise synchronise body text with original post form
		// else if(!in_index){
			$origPostForm.find('textarea[name="body"]').on('change input propertychange', function() {
				$postForm.find('textarea[name="body"]').val($(this).val());
			});
			$postForm.find('textarea[name="body"]').on('change input propertychange', function() {
				$origPostForm.find('textarea[name="body"]').val($(this).val());
			});
			$postForm.find('textarea[name="body"]').focus(function() {
				$origPostForm.find('textarea[name="body"]').removeAttr('id');
				$(this).attr('id', 'body');
			});
			$origPostForm.find('textarea[name="body"]').focus(function() {
				$postForm.find('textarea[name="body"]').removeAttr('id');
				$(this).attr('id', 'body');
			});
			// Synchronise other inputs
			$origPostForm.find('input[type="text"],select').on('change input propertychange', function() {
				$postForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
			});
			$postForm.find('input[type="text"],select').on('change input propertychange', function() {
				$origPostForm.find('[name="' + $(this).attr('name') + '"]').val($(this).val());
			});
		// }
		if (typeof $postForm.draggable != 'undefined') {
			if (localStorage.quickReplyPosition) {
				var offset = JSON.parse(localStorage.quickReplyPosition);
				if (offset.top < 0)
					offset.top = 0;
				if (offset.right > $(window).width() - $postForm.width())
					offset.right = $(window).width() - $postForm.width();
				if (offset.top > $(window).height() - $postForm.height())
					offset.top = $(window).height() - $postForm.height();
				$postForm.css('right', offset.right).css('top', offset.top);
			}
			$postForm.draggable({
				handle: 'th .handle',
				containment: 'window',
				distance: 10,
				scroll: false,
				stop: function() {
					var offset = {
						top: $(this).offset().top - $(window).scrollTop(),
						right: $(window).width() - $(this).offset().left - $(this).width(),
					};
					localStorage.quickReplyPosition = JSON.stringify(offset);

					$postForm.css('right', offset.right).css('top', offset.top).css('left', 'auto');
				}
			});
			$postForm.find('th .handle').css('cursor', 'move');
		}

		$postForm.find('th .close-btn').click(function() {
			$origPostForm.find('textarea[name="body"]').attr('id', 'body');
			$postForm.remove();
			floating_link();
		});

		// Fix bug when table gets too big for form. Shouldn't exist, but crappy CSS etc.
		$postForm.show();
		$postForm.width($postForm.find('table').width());
		$postForm.hide();

		$(window).trigger('quick-reply');
		$(window).ready(function() {
			if (!in_index && settings.get('hide_at_top', true)) {
				$(window).scroll(function() {
					if ($(this).width() <= 400)
						return;
					if ($(this).scrollTop() < $origPostForm.offset().top + $origPostForm.height() - 100)
						$postForm.fadeOut(100);
					else
						$postForm.fadeIn(100);
				}).scroll();
			} else {
				$postForm.show();
			}

			$(window).on('stylesheet', function() {
				do_css();
				if ($('link#stylesheet').attr('href')) {
					$('link#stylesheet')[0].onload = do_css;
				}
			});
		});
	};

	$(window).on('cite', function(e, id, with_link) {
		if ($(this).width() <= 400)
			return;
		var origin_id = id;
		// find the thread id for a cite if citereply call is done in index
		// pass in null if not found, it won't be evaluated in show_quick_reply
		show_quick_reply(origin_id);

		// captchaSetup();
		if (with_link) {
			$(document).ready(function() {
				if ($('#' + id).length) {
					highlightReply(id);
					$(document).scrollTop($('#' + id).offset().top);
				}

				// Honestly, I'm not sure why we need setTimeout() here, but it seems to work.
				// Same for the "tmp" variable stuff you see inside here:
				setTimeout(function() {
					var tmp = $('#quick-reply textarea[name="body"]').val();
					$('#quick-reply textarea[name="body"]').val('').focus().val(tmp);
				}, 1);
			});
		}
	});

	var floating_link = function() {
		if (!settings.get('floating_link', false))
			return;
		$('<a href="javascript:void(0)" class="quick-reply-btn">'+_('Quick Reply')+'</a>')
			.click(function() {
				show_quick_reply();
				$(this).remove();
			}).appendTo($('body'));

		$(window).on('quick-reply', function() {
			$('.quick-reply-btn').remove();
		});
	};

	if (settings.get('floating_link', false)) {
		$(window).ready(function() {
			if($('div.banner').length == 0)
				return;
			$('<style type="text/css">\
			a.quick-reply-btn {\
				position: fixed;\
				right: 0;\
				bottom: 0;\
				display: block;\
				padding: 5px 13px;\
				text-decoration: none;\
			}\
			</style>').appendTo($('head'));

			floating_link();

			if (settings.get('hide_at_top', true)) {
				$('.quick-reply-btn').hide();

				$(window).scroll(function() {
					if ($(this).width() <= 400)
						return;
					if ($(this).scrollTop() < $('form[name="post"]:first').offset().top + $('form[name="post"]:first').height() - 100)
						$('.quick-reply-btn').fadeOut(100);
					else
						$('.quick-reply-btn').fadeIn(100);
				}).scroll();
			}
		});
	}
})();

but I can't implement it again because I don't remember changes in other files like functions.php

@perdedora
Copy link
Member

It's possible, but I really don't care and also spent too much time in this shit and the last commit of active-page I realized it's not in the perfect state which I thought it was.

But hey, if you wanna do it, be my guess. Just check if the current thread in the quick-reply is the same, if not change the input thread with the new value.

@perdedora
Copy link
Member

perdedora commented Aug 30, 2024

Ok, I cared enough to try at least.
Remove the last patch of removing the quick-reply.

		if($('#quick-reply').length != 0) { // line 138
 			if ($('#quick-reply').data('thread-id') !== target_id) {
				$(`<input type='hidden' name='thread' value='${thread_id}'></input>`).appendTo($("#quick-reply"));
				$("#quick-reply .handle #thread-id-number").remove();
				const threadIdSpan = `<span id="thread-id-number">(${thread_id})</span>`;
				$("#quick-reply .handle").append(threadIdSpan);
				$("#quick-reply").attr("data-thread-id", thread_id);
			}
			return;
		}

		if (in_index) { // line 307
			$(`<input type='hidden' name='thread' value='${thread_id}'></input>`).appendTo($("#quick-reply"));
			const threadIdSpan = `<span id="thread-id-number">(${thread_id})</span>`;
			$("#quick-reply .handle").append(threadIdSpan);
			$("#quick-reply").attr("data-thread-id", thread_id);
			$("#quick-reply .form_submit").attr("value", button_reply);

			if (post_captcha == 'false') {
				$("#quick-reply .captcha").remove();
			}
		}

@chanadmins
Copy link
Author

chanadmins commented Aug 30, 2024

Ok, I cared enough to try at least. Remove the last patch of removing the quick-reply.

It works now, thanks ^^

@chanadmins
Copy link
Author

Wait, I found a bug: Reply button disapears from quick reply when thread is opened
CbunwGv

@perdedora
Copy link
Member

What do you mean? How can I reproduce?

@perdedora
Copy link
Member

Thread page:
image
Index page:
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants