mirror of https://github.com/zulip/zulip.git
stream list: Use newer code for the list cursor.
The new list_cursor class is more generic and saves the state of your cursor across redraws. Note that we no longer cycle from bottom to top or vice versa. The node test code that was removed here was kind of complex and didn't actually assert useful things after calling methods.
This commit is contained in:
parent
779535fda3
commit
e9c6f3a07d
|
@ -337,30 +337,36 @@ casper.waitForSelector('#stream_filters .highlighted_stream', function () {
|
|||
|
||||
// Use arrow keys to navigate through suggestions
|
||||
casper.then(function () {
|
||||
// Down: Denmark -> Scotland
|
||||
casper.sendKeys('.stream-list-filter', casper.page.event.key.Down, {keepFocus: true});
|
||||
// Up: Scotland -> Denmark
|
||||
casper.sendKeys('.stream-list-filter', casper.page.event.key.Up, {keepFocus: true});
|
||||
// Up: Denmark -> Verona
|
||||
casper.sendKeys('.stream-list-filter', casper.page.event.key.Up, {keepFocus: true});
|
||||
function arrow(key) {
|
||||
casper.sendKeys('.stream-list-filter',
|
||||
casper.page.event.key[key],
|
||||
{keepFocus: true});
|
||||
}
|
||||
arrow('Down'); // Denmark -> Scotland
|
||||
arrow('Up'); // Scotland -> Denmark
|
||||
arrow('Up'); // Denmark -> Denmark
|
||||
arrow('Down'); // Denmark -> Scotland
|
||||
});
|
||||
|
||||
casper.waitForSelector('#stream_filters [data-stream-name="Verona"].highlighted_stream', function () {
|
||||
casper.waitForSelector('#stream_filters [data-stream-name="Scotland"].highlighted_stream', function () {
|
||||
casper.test.info('Suggestion highlighting - after arrow key navigation');
|
||||
casper.test.assertDoesntExist('#stream_filters [data-stream-name="Denmark"].highlighted_stream',
|
||||
'Stream Denmark is not highlighted');
|
||||
casper.test.assertDoesntExist('#stream_filters [data-stream-name="Scotland"].highlighted_stream',
|
||||
'Stream Scotland is not highlighted');
|
||||
casper.test.assertExist('#stream_filters [data-stream-name="Verona"].highlighted_stream',
|
||||
'Stream Verona is highlighted');
|
||||
casper.test.assertDoesntExist(
|
||||
'#stream_filters [data-stream-name="Denmark"].highlighted_stream',
|
||||
'Stream Denmark is not highlighted');
|
||||
casper.test.assertExist(
|
||||
'#stream_filters [data-stream-name="Scotland"].highlighted_stream',
|
||||
'Stream Scotland is highlighted');
|
||||
casper.test.assertDoesntExist(
|
||||
'#stream_filters [data-stream-name="Verona"].highlighted_stream',
|
||||
'Stream Verona is not highlighted');
|
||||
});
|
||||
|
||||
// We search for the beginning of "Verona", not case sensitive
|
||||
// We search for the beginning of "Scotland", not case sensitive
|
||||
casper.then(function () {
|
||||
casper.evaluate(function () {
|
||||
$('.stream-list-filter').expectOne()
|
||||
.focus()
|
||||
.val('ver')
|
||||
.val('sCoT')
|
||||
.trigger($.Event('input'))
|
||||
.trigger($.Event('click'));
|
||||
});
|
||||
|
@ -373,16 +379,16 @@ casper.waitWhileVisible('#stream_filters [data-stream-name="Denmark"]', function
|
|||
casper.test.assertDoesntExist('#stream_filters [data-stream-name="Denmark"]',
|
||||
'Filtered stream list does not contain Denmark');
|
||||
});
|
||||
casper.waitWhileVisible('#stream_filters [data-stream-name="Scotland"]', function () {
|
||||
casper.test.assertDoesntExist('#stream_filters [data-stream-name="Scotland"]',
|
||||
'Filtered stream list does not contain Scotland');
|
||||
casper.waitWhileVisible('#stream_filters [data-stream-name="Verona"]', function () {
|
||||
casper.test.assertDoesntExist('#stream_filters [data-stream-name="Verona"]',
|
||||
'Filtered stream list does not contain Verona');
|
||||
});
|
||||
|
||||
casper.then(function () {
|
||||
casper.test.assertExists('#stream_filters [data-stream-name="Verona"]',
|
||||
'Filtered stream list does contain Verona');
|
||||
casper.test.assertExists('#stream_filters [data-stream-name="Verona"].highlighted_stream',
|
||||
'Stream Verona is highlighted');
|
||||
casper.test.assertExists('#stream_filters [data-stream-name="Scotland"]',
|
||||
'Filtered stream list does contain Scotland');
|
||||
casper.test.assertExists('#stream_filters [data-stream-name="Scotland"].highlighted_stream',
|
||||
'Stream Scotland is highlighted');
|
||||
});
|
||||
|
||||
// Clearing the list should give us back all the streams in the list
|
||||
|
|
|
@ -694,6 +694,9 @@ function make_sidebar_helper() {
|
|||
}
|
||||
|
||||
stream_list.stream_sidebar.set_row(social_stream.stream_id, row_widget());
|
||||
stream_list.stream_cursor = {
|
||||
redraw: noop,
|
||||
};
|
||||
|
||||
return {
|
||||
verify_actions: () => {
|
||||
|
|
|
@ -14,6 +14,7 @@ zrequire('narrow');
|
|||
zrequire('unread');
|
||||
zrequire('stream_data');
|
||||
zrequire('scroll_util');
|
||||
zrequire('list_cursor');
|
||||
zrequire('stream_list');
|
||||
|
||||
var noop = function () {};
|
||||
|
@ -23,6 +24,10 @@ var return_true = function () { return true; };
|
|||
set_global('topic_list', {});
|
||||
set_global('overlays', {});
|
||||
|
||||
set_global('keydown_util', {
|
||||
handle: noop,
|
||||
});
|
||||
|
||||
(function test_create_sidebar_row() {
|
||||
// Make a couple calls to create_sidebar_row() and make sure they
|
||||
// generate the right markup as well as play nice with get_stream_li().
|
||||
|
@ -280,127 +285,6 @@ function initialize_stream_data() {
|
|||
assert($('<cars sidebar row html>').hasClass('active-filter'));
|
||||
}());
|
||||
|
||||
var keydown_handler = $('.stream-list-filter').get_on_handler('keydown');
|
||||
|
||||
(function test_arrow_navigation() {
|
||||
|
||||
stream_list.build_stream_list();
|
||||
initialize_stream_data();
|
||||
|
||||
var stream_order = ['devel', 'Rome', 'test',
|
||||
'-divider-', 'announce','Denmark',
|
||||
'-divider-','cars'];
|
||||
var stream_count = 8;
|
||||
|
||||
// Mock the jquery is func
|
||||
$('.stream-list-filter').is = function (sel) {
|
||||
if (sel === ':focus') {
|
||||
return $('.stream-list-filter').is_focused();
|
||||
}
|
||||
};
|
||||
|
||||
// Mock the jquery first func
|
||||
$('#stream_filters li.narrow-filter').first = function () {
|
||||
return $('#stream_filters li[data-stream-name="' + stream_order[0] + '"]');
|
||||
};
|
||||
$('#stream_filters li.narrow-filter').last = function () {
|
||||
return $('#stream_filters li[data-stream-name="' + stream_order[stream_count - 1] + '"]');
|
||||
};
|
||||
|
||||
var sel_index = 0;
|
||||
// Returns which element is highlighted
|
||||
$('#stream_filters li.narrow-filter.highlighted_stream')
|
||||
.expectOne().data = function () {
|
||||
// Return random id (is further not used)
|
||||
return 1;
|
||||
};
|
||||
|
||||
// Returns element before highlighted one
|
||||
$('#stream_filters li.narrow-filter.highlighted_stream')
|
||||
.expectOne().prev = function () {
|
||||
if (sel_index === 0) {
|
||||
// Top, no prev element
|
||||
return $('div.no_stream');
|
||||
} else if (sel_index === 3 || sel_index === 6) {
|
||||
return $('div.divider');
|
||||
}
|
||||
return $('#stream_filters li[data-stream-name="'
|
||||
+ stream_order[sel_index-1] + '"]');
|
||||
};
|
||||
|
||||
// Returns element after highlighted one
|
||||
$('#stream_filters li.narrow-filter.highlighted_stream')
|
||||
.expectOne().next = function () {
|
||||
if (sel_index === stream_count - 1) {
|
||||
// Bottom, no next element
|
||||
return $('div.no_stream');
|
||||
} else if (sel_index === 3 || sel_index === 6) {
|
||||
return $('div.divider');
|
||||
}
|
||||
return $('#stream_filters li[data-stream-name="'
|
||||
+ stream_order[sel_index + 1] + '"]');
|
||||
};
|
||||
|
||||
for (var i = 0; i < stream_count; i = i + 1) {
|
||||
if (i === 3 || i === 6) {
|
||||
$('#stream_filters li[data-stream-name="' + stream_order[i] + '"]')
|
||||
.is = return_false;
|
||||
} else {
|
||||
$('#stream_filters li[data-stream-name="' + stream_order[i] + '"]')
|
||||
.is = return_true;
|
||||
}
|
||||
}
|
||||
|
||||
$('div.no_stream').is = return_false;
|
||||
$('div.divider').is = return_false;
|
||||
|
||||
$('#stream_filters li.narrow-filter').length = stream_count;
|
||||
|
||||
// up
|
||||
var e = {
|
||||
keyCode: 38,
|
||||
stopPropagation: function () {},
|
||||
preventDefault: function () {},
|
||||
};
|
||||
|
||||
keydown_handler(e);
|
||||
// Now the last element is highlighted
|
||||
sel_index = stream_count - 1;
|
||||
keydown_handler(e);
|
||||
sel_index = sel_index - 1;
|
||||
|
||||
// down
|
||||
e = {
|
||||
keyCode: 40,
|
||||
stopPropagation: function () {},
|
||||
preventDefault: function () {},
|
||||
};
|
||||
keydown_handler(e);
|
||||
sel_index = sel_index + 1;
|
||||
keydown_handler(e);
|
||||
}());
|
||||
|
||||
(function test_enter_press() {
|
||||
var e = {
|
||||
keyCode: 13,
|
||||
stopPropagation: function () {},
|
||||
preventDefault: function () {},
|
||||
};
|
||||
|
||||
overlays.is_active = return_false;
|
||||
narrow_state.active = return_false;
|
||||
stream_data.get_sub_by_id = function () {
|
||||
return 'name';
|
||||
};
|
||||
narrow.by = noop;
|
||||
stream_list.clear_and_hide_search = noop;
|
||||
|
||||
// Enter text and narrow users
|
||||
$(".stream-list-filter").expectOne().val('');
|
||||
|
||||
keydown_handler(e);
|
||||
}());
|
||||
|
||||
(function test_focusout_user_filter() {
|
||||
var e = { };
|
||||
var click_handler = $('.stream-list-filter').get_on_handler('focusout');
|
||||
|
|
|
@ -3,9 +3,9 @@ zrequire('stream_data');
|
|||
zrequire('stream_sort');
|
||||
var with_overrides = global.with_overrides;
|
||||
|
||||
// Test no subscribed streams
|
||||
(function test_no_subscribed_streams() {
|
||||
assert.equal(stream_sort.sort_groups(''), undefined);
|
||||
assert.equal(stream_sort.first_stream_id(), undefined);
|
||||
}());
|
||||
|
||||
const scalene = {
|
||||
|
@ -56,12 +56,25 @@ with_overrides(function (override) {
|
|||
assert.deepEqual(sorted.normal_streams, ['clarinet', 'fast tortoise']);
|
||||
assert.deepEqual(sorted.dormant_streams, ['pneumonia']);
|
||||
|
||||
// Test cursor helpers.
|
||||
assert.equal(stream_sort.first_stream_id(), scalene.stream_id);
|
||||
|
||||
assert.equal(stream_sort.prev_stream_id(scalene.stream_id), undefined);
|
||||
assert.equal(stream_sort.prev_stream_id(clarinet.stream_id), scalene.stream_id);
|
||||
|
||||
assert.equal(stream_sort.next_stream_id(fast_tortoise.stream_id), pneumonia.stream_id);
|
||||
assert.equal(stream_sort.next_stream_id(pneumonia.stream_id), undefined);
|
||||
|
||||
// Test filtering
|
||||
sorted = stream_sort.sort_groups("s");
|
||||
assert.deepEqual(sorted.pinned_streams, ['scalene']);
|
||||
assert.deepEqual(sorted.normal_streams, []);
|
||||
assert.deepEqual(sorted.dormant_streams, []);
|
||||
|
||||
assert.equal(stream_sort.prev_stream_id(clarinet.stream_id), undefined);
|
||||
|
||||
assert.equal(stream_sort.next_stream_id(clarinet.stream_id), undefined);
|
||||
|
||||
// Test searching entire word, case-insensitive
|
||||
sorted = stream_sort.sort_groups("PnEuMoNiA");
|
||||
assert.deepEqual(sorted.pinned_streams, []);
|
||||
|
|
|
@ -63,6 +63,7 @@ function get_search_term() {
|
|||
exports.remove_sidebar_row = function (stream_id) {
|
||||
exports.stream_sidebar.remove_row(stream_id);
|
||||
exports.build_stream_list();
|
||||
exports.stream_cursor.redraw();
|
||||
};
|
||||
|
||||
exports.create_initial_sidebar_rows = function () {
|
||||
|
@ -301,13 +302,7 @@ function set_stream_unread_count(stream_id, count) {
|
|||
|
||||
exports.update_streams_sidebar = function () {
|
||||
exports.build_stream_list();
|
||||
|
||||
// highlight new top stream
|
||||
$('#stream_filters li.highlighted_stream').removeClass('highlighted_stream');
|
||||
if (exports.searching()) {
|
||||
var all_streams = $('#stream_filters li.narrow-filter');
|
||||
exports.highlight_first(all_streams);
|
||||
}
|
||||
exports.stream_cursor.redraw();
|
||||
|
||||
if (! narrow_state.active()) {
|
||||
return;
|
||||
|
@ -449,43 +444,36 @@ exports.handle_narrow_deactivated = function () {
|
|||
};
|
||||
|
||||
function focus_stream_filter(e) {
|
||||
if ($('#stream_filters li.narrow-filter.highlighted_stream').length === 0) {
|
||||
// Highlight
|
||||
var all_streams = $('#stream_filters li.narrow-filter');
|
||||
exports.highlight_first(all_streams);
|
||||
}
|
||||
exports.stream_cursor.reset();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
function focusout_stream_filter() {
|
||||
// Undo highlighting
|
||||
$('#stream_filters li.narrow-filter.highlighted_stream').removeClass('highlighted_stream');
|
||||
}
|
||||
|
||||
function keydown_enter_key() {
|
||||
// Is there at least one stream?
|
||||
if ($('#stream_filters li.narrow-filter').length > 0) {
|
||||
var selected_stream_id = $('#stream_filters li.narrow-filter.highlighted_stream')
|
||||
.expectOne().data('stream-id');
|
||||
var stream_id = exports.stream_cursor.get_key();
|
||||
|
||||
var top_stream = stream_data.get_sub_by_id(selected_stream_id);
|
||||
|
||||
if (overlays.is_active()) {
|
||||
ui_util.change_tab_to('#home');
|
||||
}
|
||||
exports.clear_and_hide_search();
|
||||
narrow.by('stream', top_stream.name, {trigger: 'sidebar enter key'});
|
||||
if (stream_id === undefined) {
|
||||
// This can happen for empty searches, no need to warn.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function keydown_stream_filter(e) {
|
||||
exports.keydown_filter(e, '#stream_filters li.narrow-filter',
|
||||
$('#stream-filters-container'), keydown_enter_key);
|
||||
var sub = stream_data.get_sub_by_id(stream_id);
|
||||
|
||||
if (sub === undefined) {
|
||||
blueslip.error('Unknown stream_id for search/enter: ' + stream_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (overlays.is_active()) {
|
||||
ui_util.change_tab_to('#home');
|
||||
}
|
||||
exports.clear_and_hide_search();
|
||||
narrow.by('stream', sub.name, {trigger: 'sidebar enter key'});
|
||||
}
|
||||
|
||||
function actually_update_streams_for_search() {
|
||||
exports.update_streams_sidebar();
|
||||
resize.resize_page_components();
|
||||
exports.stream_cursor.reset();
|
||||
}
|
||||
|
||||
var update_streams_for_search = _.throttle(actually_update_streams_for_search, 50);
|
||||
|
@ -502,6 +490,7 @@ exports.initialize = function () {
|
|||
$(document).on('subscription_add_done.zulip', function (event) {
|
||||
exports.create_sidebar_row(event.sub);
|
||||
exports.build_stream_list();
|
||||
exports.stream_cursor.redraw();
|
||||
});
|
||||
|
||||
$(document).on('subscription_remove_done.zulip', function (event) {
|
||||
|
@ -527,16 +516,50 @@ exports.initialize = function () {
|
|||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$(".stream-list-filter").expectOne()
|
||||
.on('click', focus_stream_filter)
|
||||
.on('focusout', focusout_stream_filter)
|
||||
.on('input', update_streams_for_search)
|
||||
.on('keydown', keydown_stream_filter);
|
||||
$('#clear_search_stream_button').on('click', exports.clear_search);
|
||||
|
||||
$("#streams_header").expectOne().click(function (e) {
|
||||
exports.toggle_filter_displayed(e);
|
||||
});
|
||||
|
||||
exports.stream_cursor = list_cursor({
|
||||
list: {
|
||||
container: $('#stream-filters-container'),
|
||||
find_li: function (opts) {
|
||||
var stream_id = opts.key;
|
||||
var li = exports.get_stream_li(stream_id);
|
||||
return li;
|
||||
},
|
||||
first_key: stream_sort.first_stream_id,
|
||||
prev_key: stream_sort.prev_stream_id,
|
||||
next_key: stream_sort.next_stream_id,
|
||||
},
|
||||
highlight_class: 'highlighted_stream',
|
||||
});
|
||||
|
||||
var $search_input = $('.stream-list-filter').expectOne();
|
||||
|
||||
keydown_util.handle({
|
||||
elem: $search_input,
|
||||
handlers: {
|
||||
enter_key: function () {
|
||||
keydown_enter_key();
|
||||
return true;
|
||||
},
|
||||
up_arrow: function () {
|
||||
exports.stream_cursor.prev();
|
||||
return true;
|
||||
},
|
||||
down_arrow: function () {
|
||||
exports.stream_cursor.next();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
$search_input.on('click', focus_stream_filter);
|
||||
$search_input.on('focusout', exports.stream_cursor.clear);
|
||||
$search_input.on('input', update_streams_for_search);
|
||||
};
|
||||
|
||||
exports.searching = function () {
|
||||
|
@ -573,9 +596,7 @@ exports.initiate_search = function () {
|
|||
}
|
||||
filter.focus();
|
||||
|
||||
// Highlight first result
|
||||
var all_streams = $('#stream_filters li.narrow-filter');
|
||||
exports.highlight_first(all_streams);
|
||||
exports.stream_cursor.reset();
|
||||
};
|
||||
|
||||
exports.clear_and_hide_search = function () {
|
||||
|
@ -584,94 +605,11 @@ exports.clear_and_hide_search = function () {
|
|||
filter.val('');
|
||||
update_streams_for_search();
|
||||
}
|
||||
exports.stream_cursor.clear();
|
||||
filter.blur();
|
||||
filter.parent().addClass('notdisplayed');
|
||||
};
|
||||
|
||||
function next_sibing_in_dir(elm, dir_up) {
|
||||
if (dir_up) {
|
||||
return elm.prev();
|
||||
}
|
||||
return elm.next();
|
||||
}
|
||||
|
||||
function keydown_arrow_key(dir_up, all_streams_selector, scroll_container) {
|
||||
// Are there streams to cyle through?
|
||||
if ($(all_streams_selector).length > 0) {
|
||||
var current_sel = $(all_streams_selector + '.highlighted_stream').expectOne();
|
||||
var next_sibling = next_sibing_in_dir(current_sel, dir_up);
|
||||
|
||||
if (next_sibling.is('hr.stream-split')) {
|
||||
// Skip separator
|
||||
next_sibling = next_sibing_in_dir(next_sibling, dir_up);
|
||||
}
|
||||
|
||||
if (!next_sibling.is('li.narrow-filter')) {
|
||||
// At the every bottom or top
|
||||
var all_streams = $(all_streams_selector);
|
||||
if (dir_up) {
|
||||
// top -> start at the bottom
|
||||
next_sibling = all_streams.last();
|
||||
} else {
|
||||
// bottom -> start at the top
|
||||
next_sibling = all_streams.first();
|
||||
}
|
||||
}
|
||||
|
||||
// Classes must be explicitly named
|
||||
current_sel.removeClass('highlighted_stream');
|
||||
next_sibling.addClass('highlighted_stream');
|
||||
|
||||
scroll_util.scroll_element_into_container(next_sibling, scroll_container);
|
||||
}
|
||||
}
|
||||
|
||||
exports.keydown_filter = function (e, all_streams_selector, scroll_container,
|
||||
enter_press_function) {
|
||||
// Function for left and right sidebar
|
||||
// Could be placed somewhere else but ui.js is already very full
|
||||
|
||||
// Catch <enter> and <up-arrow>, <down-arrow> key presses
|
||||
var handled = false;
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 13: {
|
||||
// Enter key was pressed
|
||||
enter_press_function();
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case 38: {
|
||||
// Up-arrow key was pressed
|
||||
keydown_arrow_key(true, all_streams_selector, scroll_container);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
// Down-arrow key was pressed
|
||||
keydown_arrow_key(false, all_streams_selector, scroll_container);
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
// Since we already handled the key event above, suppress the browser handling it.
|
||||
// We don't want the cursor to move when <arrow-up/down> is pressed.
|
||||
// In the <enter> case:
|
||||
// Prevent a newline from being entered into the soon-to-be-opened composebox
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
exports.highlight_first = function (all_streams) {
|
||||
if (all_streams.length > 0) {
|
||||
// Classes must be explicitly named
|
||||
all_streams.first().addClass('highlighted_stream');
|
||||
}
|
||||
};
|
||||
|
||||
exports.toggle_filter_displayed = function (e) {
|
||||
if (e.target.id === 'streams_inline_cog') {
|
||||
return;
|
||||
|
|
|
@ -91,6 +91,52 @@ exports.sort_groups = function (search_term) {
|
|||
};
|
||||
};
|
||||
|
||||
function pos(stream_id) {
|
||||
var sub = stream_data.get_sub_by_id(stream_id);
|
||||
var name = sub.name;
|
||||
var i = all_streams.indexOf(name);
|
||||
|
||||
if (i < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
function maybe_get_stream_id(i) {
|
||||
if (i < 0 || i >= all_streams.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var name = all_streams[i];
|
||||
var stream_id = stream_data.get_stream_id(name);
|
||||
return stream_id;
|
||||
}
|
||||
|
||||
exports.first_stream_id = function () {
|
||||
return maybe_get_stream_id(0);
|
||||
};
|
||||
|
||||
exports.prev_stream_id = function (stream_id) {
|
||||
var i = pos(stream_id);
|
||||
|
||||
if (i === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return maybe_get_stream_id(i - 1);
|
||||
};
|
||||
|
||||
exports.next_stream_id = function (stream_id) {
|
||||
var i = pos(stream_id);
|
||||
|
||||
if (i === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
return maybe_get_stream_id(i + 1);
|
||||
};
|
||||
|
||||
return exports;
|
||||
}());
|
||||
if (typeof module !== 'undefined') {
|
||||
|
|
Loading…
Reference in New Issue