From 3e11337b3b4c36df20d5565d60d05995b0314363 Mon Sep 17 00:00:00 2001
From: Julius Härtl <jus@bitgrid.net>
Date: Thu, 8 Nov 2018 13:30:39 +0100
Subject: Allow sorting in the file picker
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Julius Härtl <jus@bitgrid.net>
---
 core/css/styles.scss           | 41 +++++++++++++++++++++++++++++++++
 core/js/oc-dialogs.js          | 52 +++++++++++++++++++++++++++++++++++++-----
 core/templates/filepicker.html | 42 ++++++++++++++++++++++++++--------
 3 files changed, 119 insertions(+), 16 deletions(-)

(limited to 'core')

diff --git a/core/css/styles.scss b/core/css/styles.scss
index 9b7da393698..62d2bb45c96 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -773,6 +773,47 @@ code {
 		margin-bottom: 50px;
 	}
 	.filelist {
+		thead {
+			tr {
+				border-bottom: 1px solid var(--color-border);
+				background-color: var(--color-main-background);
+				th {
+					width: auto;
+					border: none;
+				}
+			}
+		}
+		th .columntitle {
+			display: block;
+			padding: 15px;
+			height: 50px;
+			box-sizing: border-box;
+			-moz-box-sizing: border-box;
+			vertical-align: middle;
+		}
+		th .columntitle.name {
+			padding-left: 5px;
+			margin-left: 50px;
+		}
+
+		th .sort-indicator {
+			width: 10px;
+			height: 8px;
+			margin-left: 5px;
+			display: inline-block;
+			vertical-align: text-bottom;
+			opacity: .3;
+		}
+		.sort-indicator.hidden,
+		th:hover .sort-indicator.hidden,
+		th:focus .sort-indicator.hidden {
+			visibility: hidden;
+		}
+		th:hover .sort-indicator.hidden,
+		th:focus .sort-indicator.hidden {
+			visibility: visible;
+		}
+
 		td {
 			padding: 14px;
 			border-bottom: 1px solid var(--color-border);
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index dee74502a6f..e46dd9a57ea 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -192,6 +192,9 @@ var OCdialogs = {
 	*/
 	filepicker:function(title, callback, multiselect, mimetypeFilter, modal, type) {
 		var self = this;
+
+		this.filepicker.sortField = 'name';
+		this.filepicker.sortOrder = 'asc';
 		// avoid opening the picker twice
 		if (this.filepicker.loading) {
 			return;
@@ -252,6 +255,7 @@ var OCdialogs = {
 			}
 
 			self.$filePicker.ready(function() {
+				self.$fileListHeader = self.$filePicker.find('.filelist thead tr');
 				self.$filelist = self.$filePicker.find('.filelist tbody');
 				self.$dirTree = self.$filePicker.find('.dirtree');
 				self.$dirTree.on('click', 'div:not(:last-child)', self, function (event) {
@@ -260,6 +264,12 @@ var OCdialogs = {
 				self.$filelist.on('click', 'tr', function(event) {
 					self._handlePickerClick(event, $(this), type);
 				});
+				self.$fileListHeader.on('click', 'a', function(event) {
+					var dir = self.$filePicker.data('path');
+					self.filepicker.sortField = $(event.currentTarget).data('sort');
+					self.filepicker.sortOrder = self.filepicker.sortOrder === 'asc' ? 'desc' : 'asc';
+					self._fillFilePicker(dir);
+				});
 				self._fillFilePicker('');
 			});
 
@@ -824,7 +834,7 @@ var OCdialogs = {
 			var self = this;
 			$.get(OC.filePath('core', 'templates', 'filepicker.html'), function(tmpl) {
 				self.$filePickerTemplate = $(tmpl);
-				self.$listTmpl = self.$filePickerTemplate.find('.filelist tr:first-child').detach();
+				self.$listTmpl = self.$filePickerTemplate.find('.filelist tbody tr:first-child').detach();
 				defer.resolve(self.$filePickerTemplate);
 			})
 			.fail(function(jqXHR, textStatus, errorThrown) {
@@ -892,20 +902,50 @@ var OCdialogs = {
 		if (typeof(filter) === "string") {
 			filter = [filter];
 		}
+		self.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s');
+		self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden');
+		if (self.filepicker.sortOrder === 'asc') {
+			self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s');
+		} else {
+			self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n');
+		}
 		self.filepicker.filesClient.getFolderContents(dir).then(function(status, files) {
 			if (filter && filter.length > 0 && filter.indexOf('*') === -1) {
 				files = files.filter(function (file) {
 					return file.type === 'dir' || filter.indexOf(file.mimetype) !== -1;
 				});
 			}
-			files = files.sort(function(a, b) {
-				if (a.type === 'dir' && b.type !== 'dir') {
+
+			var Comparators = {
+				name: function(fileInfo1, fileInfo2) {
+					if (fileInfo1.type === 'dir' && fileInfo2.type !== 'dir') {
+						return -1;
+					}
+					if (fileInfo1.type !== 'dir' && fileInfo2.type === 'dir') {
+						return 1;
+					}
+					return OC.Util.naturalSortCompare(fileInfo1.name, fileInfo2.name);
+				},
+				size: function(fileInfo1, fileInfo2) {
+					return fileInfo1.size - fileInfo2.size;
+				},
+				mtime: function(fileInfo1, fileInfo2) {
+					return fileInfo1.mtime - fileInfo2.mtime;
+				}
+			};
+			var comparator = Comparators[self.filepicker.sortField] || Comparators.name;
+			files = files.sort(function(file1, file2) {
+				var isFavorite = function(fileInfo) {
+					return fileInfo.tags && fileInfo.tags.indexOf(OC.TAG_FAVORITE) >= 0;
+				};
+
+				if (isFavorite(file1) && !isFavorite(file2)) {
 					return -1;
-				} else if(a.type !== 'dir' && b.type === 'dir') {
+				} else if (!isFavorite(file1) && isFavorite(file2)) {
 					return 1;
-				} else {
-					return a.name.localeCompare(b.name, undefined, {numeric: true});
 				}
+
+				return self.filepicker.sortOrder === 'asc' ? comparator(file1, file2) : -comparator(file1, file2);
 			});
 
 			self._fillSlug();
diff --git a/core/templates/filepicker.html b/core/templates/filepicker.html
index 4c66dfc707b..090e6bce773 100644
--- a/core/templates/filepicker.html
+++ b/core/templates/filepicker.html
@@ -8,17 +8,39 @@
 			<h2>{emptytext}</h2>
 		</div>
 		<table id="filestable" class="filelist list-container view-grid">
+			<thead>
+				<tr>
+					<th id="headerName" class="column-name">
+						<div id="headerName-container">
+							<a class="name sort columntitle" data-sort="name">
+								<span>Name</span>
+								<span class="sort-indicator hidden icon-triangle-n"></span>
+							</a>
+						</div>
+					</th>
+					<th id="headerSize" class="column-size">
+						<a class="size sort columntitle" data-sort="size">
+							<span>Size</span>
+							<span class="sort-indicator hidden icon-triangle-n"></span></a>
+					</th>
+					<th id="headerDate" class="column-mtime">
+						<a id="modified" class="columntitle" data-sort="mtime">
+							<span>Modified</span>
+							<span class="sort-indicator hidden icon-triangle-n"></span></a>
+					</th>
+				</tr>
+			</thead>
 			<tbody>
-			<tr data-entryname="{filename}" data-type="{type}">
-				<td class="filename"
-					style="background-image:url({icon})">{filename}
-				</td>
-				<td class="filesize"
-					style="color:rgb({sizeColor}, {sizeColor}, {sizeColor})">
-					{size}
-				</td>
-				<td class="date">{date}</td>
-			</tr>
+				<tr data-entryname="{filename}" data-type="{type}">
+					<td class="filename"
+						style="background-image:url({icon})">{filename}
+					</td>
+					<td class="filesize"
+						style="color:rgb({sizeColor}, {sizeColor}, {sizeColor})">
+						{size}
+					</td>
+					<td class="date">{date}</td>
+				</tr>
 			</tbody>
 		</table>
 	</div>
-- 
cgit v1.2.3


From 916cdb260ef5fd35cb671468a93044d8decb2ee3 Mon Sep 17 00:00:00 2001
From: Julius Härtl <jus@bitgrid.net>
Date: Thu, 8 Nov 2018 13:32:10 +0100
Subject: Flip sort indicators
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Julius Härtl <jus@bitgrid.net>
---
 core/js/oc-dialogs.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'core')

diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index e46dd9a57ea..49bf626ed82 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -905,9 +905,9 @@ var OCdialogs = {
 		self.$fileListHeader.find('.sort-indicator').addClass('hidden').removeClass('icon-triangle-n').removeClass('icon-triangle-s');
 		self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').removeClass('hidden');
 		if (self.filepicker.sortOrder === 'asc') {
-			self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s');
-		} else {
 			self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-n');
+		} else {
+			self.$fileListHeader.find('[data-sort=' + self.filepicker.sortField + '] .sort-indicator').addClass('icon-triangle-s');
 		}
 		self.filepicker.filesClient.getFolderContents(dir).then(function(status, files) {
 			if (filter && filter.length > 0 && filter.indexOf('*') === -1) {
-- 
cgit v1.2.3


From 5e5cced48cc912f3dcd71013483f62dbb56d7362 Mon Sep 17 00:00:00 2001
From: Julius Härtl <jus@bitgrid.net>
Date: Fri, 16 Nov 2018 14:50:40 +0100
Subject: Properly align loading indicator when switching folders
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Julius Härtl <jus@bitgrid.net>
---
 core/css/styles.scss  | 2 +-
 core/js/oc-dialogs.js | 9 +++++++--
 2 files changed, 8 insertions(+), 3 deletions(-)

(limited to 'core')

diff --git a/core/css/styles.scss b/core/css/styles.scss
index 62d2bb45c96..da270893e66 100644
--- a/core/css/styles.scss
+++ b/core/css/styles.scss
@@ -778,7 +778,7 @@ code {
 				border-bottom: 1px solid var(--color-border);
 				background-color: var(--color-main-background);
 				th {
-					width: auto;
+					width: 80%;
 					border: none;
 				}
 			}
diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js
index 49bf626ed82..3169dab4b88 100644
--- a/core/js/oc-dialogs.js
+++ b/core/js/oc-dialogs.js
@@ -257,6 +257,7 @@ var OCdialogs = {
 			self.$filePicker.ready(function() {
 				self.$fileListHeader = self.$filePicker.find('.filelist thead tr');
 				self.$filelist = self.$filePicker.find('.filelist tbody');
+				self.$filelistContainer = self.$filePicker.find('.filelist-container');
 				self.$dirTree = self.$filePicker.find('.dirtree');
 				self.$dirTree.on('click', 'div:not(:last-child)', self, function (event) {
 					self._handleTreeListSelect(event, type);
@@ -896,7 +897,9 @@ var OCdialogs = {
 	*/
 	_fillFilePicker:function(dir) {
 		var self = this;
-		this.$filelist.empty().addClass('icon-loading');
+		this.$filelist.empty();
+		this.$filePicker.find('.emptycontent').hide();
+		this.$filelistContainer.addClass('icon-loading');
 		this.$filePicker.data('path', dir);
 		var filter = this.$filePicker.data('mimetype');
 		if (typeof(filter) === "string") {
@@ -952,8 +955,10 @@ var OCdialogs = {
 
 			if (files.length === 0) {
 				self.$filePicker.find('.emptycontent').show();
+				self.$fileListHeader.hide();
 			} else {
 				self.$filePicker.find('.emptycontent').hide();
+				self.$fileListHeader.show();
 			}
 
 			$.each(files, function(idx, entry) {
@@ -993,7 +998,7 @@ var OCdialogs = {
 				self.$filelist.append($row);
 			});
 
-			self.$filelist.removeClass('icon-loading');
+			self.$filelistContainer.removeClass('icon-loading');
 		});
 	},
 	/**
-- 
cgit v1.2.3