Better QnA ajax insert - work in progress

This commit is contained in:
Zach Copley 2011-04-15 15:07:17 -07:00
parent 6bf29ff684
commit a610836e1c
3 changed files with 248 additions and 37 deletions

View File

@ -49,7 +49,7 @@ class QnanewanswerAction extends Action
protected $error = null; protected $error = null;
protected $complete = null; protected $complete = null;
protected $question = null; public $question = null;
protected $content = null; protected $content = null;
/** /**
@ -162,30 +162,10 @@ class QnanewanswerAction extends Action
$this->elementStart('body'); $this->elementStart('body');
$nli = new NoticeListItem($notice, $this);
$nli = new NoticeAnswerListItem($notice, $this);
$nli->show(); $nli->show();
//$this->raw($answer->asHTML());
/*
$question = $this->question;
$nli = new NoticeListItem($notice, $this);
$nli->showNotice();
$this->elementStart('div', array('class' => 'entry-content answer-content'));
if (!empty($answer)) {
$form = new QnashowanswerForm($this, $answer);
$form->show();
} else {
$this->text(_m('Answer data is missing.'));
}
$this->elementEnd('div');
// @fixme
//$this->elementStart('div', array('class' => 'entry-content'));
*/
$this->elementEnd('body'); $this->elementEnd('body');
$this->elementEnd('html'); $this->elementEnd('html');
} else { } else {
@ -299,3 +279,41 @@ class QnanewanswerAction extends Action
} }
} }
class NoticeAnswerListItem extends NoticeListItem
{
protected $question;
/**
* constructor
*
* Also initializes the profile attribute.
*
* @param Notice $notice The notice we'll display
*/
function __construct($notice, $out=null)
{
parent::__construct($notice, $out);
$this->question = $out->question;
}
function show()
{
if (empty($this->notice)) {
common_log(LOG_WARNING, "Trying to show missing notice; skipping.");
return;
} else if (empty($this->profile)) {
common_log(LOG_WARNING, "Trying to show missing profile (" . $this->notice->profile_id . "); skipping.");
return;
}
$this->showStart();
$this->showNotice();
$this->showNoticeInfo();
$notice = $this->question->getNotice();
$this->out->hidden('inreplyto', $notice->id);
$this->showEnd();
}
}

View File

@ -24,9 +24,6 @@ var QnA = {
$('input[name=best]').live('click', function() { $('input[name=best]').live('click', function() {
that.close(this); that.close(this);
}); });
}, },
/** /**
@ -88,8 +85,10 @@ var QnA = {
var openAnswers = $('li.notice-answer'); var openAnswers = $('li.notice-answer');
if (openAnswers.length > 0) { if (openAnswers.length > 0) {
console.log("Found and open answer to close");
var target = $(e.target); var target = $(e.target);
openAnswers.each(function() { openAnswers.each(function() {
console.log("found an open answer");
// Did we click outside this one? // Did we click outside this one?
var answerItem = $(this); var answerItem = $(this);
if (answerItem.has(e.target).length == 0) { if (answerItem.has(e.target).length == 0) {
@ -107,6 +106,7 @@ var QnA = {
}); });
text.focus(); text.focus();
console.log('finished dealing with body click');
}; };
placeholder.hide(); placeholder.hide();
@ -159,15 +159,6 @@ var QnA = {
}, },
AnswerFormSetup: function(form) {
console.log("AnswerFormSetup - begin");
if (!form.data('AnswerFormSetup')) {
form.data('AnswerFormSetup', true);
}
console.log("AnswerFormSetup - exit");
},
/** /**
* Setup function -- DOES NOT apply immediately. * Setup function -- DOES NOT apply immediately.
* *
@ -183,6 +174,208 @@ var QnA = {
return false; return false;
}); });
console.log("NoticeInlineAnswerSetup - exit"); console.log("NoticeInlineAnswerSetup - exit");
},
AnswerFormSetup: function(form) {
console.log("AnswerFormSetup");
$('input[type=submit]').live('click', function() {
console.log("AnswerFormSetup click");
QnA.FormAnswerXHR(form);
});
},
/**
* Setup function -- DOES NOT trigger actions immediately.
*
* Sets up event handlers for special-cased async submission of the
* notice-posting form, including some pre-post validation.
*
* Unlike FormXHR() this does NOT submit the form immediately!
* It sets up event handlers so that any method of submitting the
* form (click on submit button, enter, submit() etc) will trigger
* it properly.
*
* Also unlike FormXHR(), this system will use a hidden iframe
* automatically to handle file uploads via <input type="file">
* controls.
*
* @fixme tl;dr
* @fixme vast swaths of duplicate code and really long variable names clutter this function up real bad
* @fixme error handling is unreliable
* @fixme cookieValue is a global variable, but probably shouldn't be
* @fixme saving the location cache cookies should be split out
* @fixme some error messages are hardcoded english: needs i18n
* @fixme special-case for bookmarklet is confusing and uses a global var "self". Is this ok?
*
* @param {jQuery} form: jQuery object whose first element is a form
*
* @access public
*/
FormAnswerXHR: function(form) {
console.log("FormAanwerXHR - begin");
//SN.C.I.NoticeDataGeo = {};
form.append('<input type="hidden" name="ajax" value="1"/>');
console.log("appended ajax flag");
// Make sure we don't have a mixed HTTP/HTTPS submission...
form.attr('action', SN.U.RewriteAjaxAction(form.attr('action')));
console.log("rewrote action");
/**
* Show a response feedback bit under the new-notice dialog.
*
* @param {String} cls: CSS class name to use ('error' or 'success')
* @param {String} text
* @access private
*/
var showFeedback = function(cls, text) {
form.append(
$('<p class="form_response"></p>')
.addClass(cls)
.text(text)
);
};
/**
* Hide the previous response feedback, if any.
*/
var removeFeedback = function() {
form.find('.form_response').remove();
};
console.log("doing ajax");
form.ajaxForm({
dataType: 'xml',
timeout: '60000',
beforeSend: function(formData) {
console.log("beforeSend");
if (form.find('.notice_data-text:first').val() == '') {
form.addClass(SN.C.S.Warning);
return false;
}
form
.addClass(SN.C.S.Processing)
.find('.submit')
.addClass(SN.C.S.Disabled)
.attr(SN.C.S.Disabled, SN.C.S.Disabled);
SN.U.normalizeGeoData(form);
return true;
},
error: function (xhr, textStatus, errorThrown) {
form
.removeClass(SN.C.S.Processing)
.find('.submit')
.removeClass(SN.C.S.Disabled)
.removeAttr(SN.C.S.Disabled, SN.C.S.Disabled);
removeFeedback();
if (textStatus == 'timeout') {
// @fixme i18n
showFeedback('error', 'Sorry! We had trouble sending your notice. The servers are overloaded. Please try again, and contact the site administrator if this problem persists.');
}
else {
var response = SN.U.GetResponseXML(xhr);
if ($('.'+SN.C.S.Error, response).length > 0) {
form.append(document._importNode($('.'+SN.C.S.Error, response)[0], true));
}
else {
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
form
.resetForm()
.find('.attach-status').remove();
SN.U.FormNoticeEnhancements(form);
}
else {
// @fixme i18n
showFeedback('error', '(Sorry! We had trouble sending your notice ('+xhr.status+' '+xhr.statusText+'). Please report the problem to the site administrator if this happens again.');
}
}
}
},
success: function(data, textStatus) {
console.log("FormAnswerHXR success");
removeFeedback();
var errorResult = $('#'+SN.C.S.Error, data);
if (errorResult.length > 0) {
showFeedback('error', errorResult.text());
}
else {
// New notice post was successful. If on our timeline, show it!
var notice = document._importNode($('li', data)[0], true);
var notices = $('#notices_primary .notices:first');
var replyItem = form.closest('li.notice-reply');
if (replyItem.length > 0) {
console.log("I found a reply li to append to");
// If this is an inline reply, remove the form...
var list = form.closest('.threaded-replies');
var placeholder = list.find('.notice-answer-placeholder');
replyItem.remove();
var id = $(notice).attr('id');
console.log("got notice id " + id);
if ($("#"+id).length == 0) {
console.log("inserting before placeholder");
$(notice).insertBefore(placeholder);
} else {
// Realtime came through before us...
}
// ...and show the placeholder form.
placeholder.show();
console.log('qqqq made it this far')
} else if (notices.length > 0 && SN.U.belongsOnTimeline(notice)) {
// Not a reply. If on our timeline, show it at the top!
if ($('#'+notice.id).length === 0) {
console.log("couldn't find a notice id for " + notice.id);
var notice_irt_value = form.find('#inreplyto').val();
var notice_irt = '#notices_primary #notice-'+notice_irt_value;
console.log("notice_irt value = " + notice_irt_value);
if($('body')[0].id == 'conversation') {
console.log("found conversation");
if(notice_irt_value.length > 0 && $(notice_irt+' .notices').length < 1) {
$(notice_irt).append('<ul class="notices"></ul>');
}
$($(notice_irt+' .notices')[0]).append(notice);
}
else {
console.log("prepending notice")
notices.prepend(notice);
}
$('#'+notice.id)
.css({display:'none'})
.fadeIn(2500);
}
} else {
// Not on a timeline that this belongs on?
// Just show a success message.
// @fixme inline
showFeedback('success', $('title', data).text());
}
//form.resetForm();
//SN.U.FormNoticeEnhancements(form);
}
},
complete: function(xhr, textStatus) {
form
.removeClass(SN.C.S.Processing)
.find('.submit')
.removeAttr(SN.C.S.Disabled)
.removeClass(SN.C.S.Disabled);
form.find('[name=lat]').val(SN.C.I.NoticeDataGeo.NLat);
form.find('[name=lon]').val(SN.C.I.NoticeDataGeo.NLon);
form.find('[name=location_ns]').val(SN.C.I.NoticeDataGeo.NLNS);
form.find('[name=location_id]').val(SN.C.I.NoticeDataGeo.NLID);
form.find('[name=notice_data-geo]').attr('checked', SN.C.I.NoticeDataGeo.NDG);
}
});
} }
}; };

View File

@ -81,7 +81,7 @@ class QnanewanswerForm extends Form
*/ */
function formClass() function formClass()
{ {
return 'form_settings ajax'; return 'form_settings qna_answer_form';
} }
/** /**