Feature: support sorting sidebar saved views (#4381)

This commit is contained in:
shamoon 2023-10-19 19:41:01 -07:00 committed by GitHub
parent 999ae678c2
commit 9880f9ebc7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 397 additions and 147 deletions

View File

@ -421,11 +421,11 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">207</context>
<context context-type="linenumber">218</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">210</context>
<context context-type="linenumber">221</context>
</context-group>
</trans-unit>
<trans-unit id="3894950702316166331" datatype="html">
@ -511,11 +511,11 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">185</context>
<context context-type="linenumber">196</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">188</context>
<context context-type="linenumber">199</context>
</context-group>
</trans-unit>
<trans-unit id="1685061484835793745" datatype="html">
@ -1355,67 +1355,67 @@
<source>Saved view &quot;<x id="PH" equiv-text="savedView.name"/>&quot; deleted.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">362</context>
<context context-type="linenumber">370</context>
</context-group>
</trans-unit>
<trans-unit id="3891152409365583719" datatype="html">
<source>Settings saved</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">484</context>
<context context-type="linenumber">492</context>
</context-group>
</trans-unit>
<trans-unit id="7217000812750597833" datatype="html">
<source>Settings were saved successfully.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">485</context>
<context context-type="linenumber">493</context>
</context-group>
</trans-unit>
<trans-unit id="525012668859298131" datatype="html">
<source>Settings were saved successfully. Reload is required to apply some changes.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">489</context>
<context context-type="linenumber">497</context>
</context-group>
</trans-unit>
<trans-unit id="8491974984518503778" datatype="html">
<source>Reload now</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">490</context>
<context context-type="linenumber">498</context>
</context-group>
</trans-unit>
<trans-unit id="3011185103048412841" datatype="html">
<source>An error occurred while saving settings.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">500</context>
<context context-type="linenumber">508</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
<context context-type="linenumber">104</context>
<context context-type="linenumber">114</context>
</context-group>
</trans-unit>
<trans-unit id="6839066544204061364" datatype="html">
<source>Use system language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">508</context>
<context context-type="linenumber">516</context>
</context-group>
</trans-unit>
<trans-unit id="7729897675462249787" datatype="html">
<source>Use date format of display language</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">515</context>
<context context-type="linenumber">523</context>
</context-group>
</trans-unit>
<trans-unit id="5260584511980773458" datatype="html">
<source>Error while storing settings on server.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/admin/settings/settings.component.ts</context>
<context context-type="linenumber">538</context>
<context context-type="linenumber">546</context>
</context-group>
</trans-unit>
<trans-unit id="2991443309752293110" datatype="html">
@ -1426,7 +1426,7 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">199</context>
<context context-type="linenumber">210</context>
</context-group>
</trans-unit>
<trans-unit id="103921551219467537" datatype="html">
@ -1635,11 +1635,11 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">192</context>
<context context-type="linenumber">203</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">195</context>
<context context-type="linenumber">206</context>
</context-group>
</trans-unit>
<trans-unit id="4555457172864212828" datatype="html">
@ -1969,11 +1969,11 @@
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">214</context>
<context context-type="linenumber">225</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">217</context>
<context context-type="linenumber">228</context>
</context-group>
</trans-unit>
<trans-unit id="6570363013146073520" datatype="html">
@ -2026,36 +2026,36 @@
<source>Open documents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">107</context>
<context context-type="linenumber">118</context>
</context-group>
</trans-unit>
<trans-unit id="5687256342387781369" datatype="html">
<source>Close all</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">123</context>
<context context-type="linenumber">134</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">126</context>
<context context-type="linenumber">137</context>
</context-group>
</trans-unit>
<trans-unit id="3897348120591552265" datatype="html">
<source>Manage</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">133</context>
<context context-type="linenumber">144</context>
</context-group>
</trans-unit>
<trans-unit id="7437910965833684826" datatype="html">
<source>Correspondents</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">137</context>
<context context-type="linenumber">148</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">140</context>
<context context-type="linenumber">151</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html</context>
@ -2066,11 +2066,11 @@
<source>Tags</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">144</context>
<context context-type="linenumber">155</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">147</context>
<context context-type="linenumber">158</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/common/input/tags/tags.component.ts</context>
@ -2097,102 +2097,116 @@
<source>Document types</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">151</context>
<context context-type="linenumber">162</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">154</context>
<context context-type="linenumber">165</context>
</context-group>
</trans-unit>
<trans-unit id="8835528846812581148" datatype="html">
<source>Storage paths</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">158</context>
<context context-type="linenumber">169</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">161</context>
<context context-type="linenumber">172</context>
</context-group>
</trans-unit>
<trans-unit id="4462691404891390153" datatype="html">
<source>Consumption templates</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">165</context>
<context context-type="linenumber">176</context>
</context-group>
</trans-unit>
<trans-unit id="5433675495457939071" datatype="html">
<source>Templates</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">168</context>
<context context-type="linenumber">179</context>
</context-group>
</trans-unit>
<trans-unit id="1292737233370901804" datatype="html">
<source>Mail</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">172</context>
<context context-type="linenumber">183</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">175</context>
<context context-type="linenumber">186</context>
</context-group>
</trans-unit>
<trans-unit id="7844706011418789951" datatype="html">
<source>Administration</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">181</context>
<context context-type="linenumber">192</context>
</context-group>
</trans-unit>
<trans-unit id="5537285341303594392" datatype="html">
<source>File Tasks<x id="START_TAG_SPAN_1" ctype="x-span_1" equiv-text="&lt;span *ngIf=&quot;tasksService.failedFileTasks.length &gt; 0&quot;&gt;"/><x id="START_TAG_SPAN" ctype="x-span" equiv-text="&lt;span class=&quot;badge bg-danger ms-2&quot;&gt;"/><x id="INTERPOLATION" equiv-text="{{tasksService.failedFileTasks.length}}"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/><x id="CLOSE_TAG_SPAN" ctype="x-span" equiv-text="&lt;/span&gt;"/></source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">203</context>
<context context-type="linenumber">214</context>
</context-group>
</trans-unit>
<trans-unit id="1534029177398918729" datatype="html">
<source>GitHub</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">223</context>
<context context-type="linenumber">234</context>
</context-group>
</trans-unit>
<trans-unit id="4112664765954374539" datatype="html">
<source>is available.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">229</context>
<context context-type="linenumber">240</context>
</context-group>
</trans-unit>
<trans-unit id="1175891574282637937" datatype="html">
<source>Click to view.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">229</context>
<context context-type="linenumber">240</context>
</context-group>
</trans-unit>
<trans-unit id="9811291095862612" datatype="html">
<source>Paperless-ngx can automatically check for updates</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">233</context>
<context context-type="linenumber">244</context>
</context-group>
</trans-unit>
<trans-unit id="894819944961861800" datatype="html">
<source> How does this work? </source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">240,242</context>
<context context-type="linenumber">251,253</context>
</context-group>
</trans-unit>
<trans-unit id="509090351011426949" datatype="html">
<source>Update available</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.html</context>
<context context-type="linenumber">262</context>
</context-group>
</trans-unit>
<trans-unit id="1542489069631984294" datatype="html">
<source>Sidebar views updated</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
<context context-type="linenumber">248</context>
</context-group>
</trans-unit>
<trans-unit id="3547923076537026828" datatype="html">
<source>Error updating sidebar views</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
<context context-type="linenumber">251</context>
</context-group>
</trans-unit>
@ -2200,7 +2214,7 @@
<source>An error occurred while saving update checking settings.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/app-frame/app-frame.component.ts</context>
<context context-type="linenumber">237</context>
<context context-type="linenumber">272</context>
</context-group>
</trans-unit>
<trans-unit id="8700121026680200191" datatype="html">
@ -3612,28 +3626,28 @@
<source>Hello <x id="PH" equiv-text="this.settingsService.displayName"/>, welcome to Paperless-ngx</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">53</context>
<context context-type="linenumber">38</context>
</context-group>
</trans-unit>
<trans-unit id="5334686081082652461" datatype="html">
<source>Welcome to Paperless-ngx</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">55</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="1325877348738783391" datatype="html">
<source>Dashboard updated</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">86</context>
<context context-type="linenumber">71</context>
</context-group>
</trans-unit>
<trans-unit id="3214475953924351473" datatype="html">
<source>Error updating dashboard</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/components/dashboard/dashboard.component.ts</context>
<context context-type="linenumber">89</context>
<context context-type="linenumber">74</context>
</context-group>
</trans-unit>
<trans-unit id="2946624699882754313" datatype="html">
@ -5999,224 +6013,224 @@
<source>English (US)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">154</context>
<context context-type="linenumber">155</context>
</context-group>
</trans-unit>
<trans-unit id="7318555235181361185" datatype="html">
<source>Afrikaans</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">160</context>
<context context-type="linenumber">161</context>
</context-group>
</trans-unit>
<trans-unit id="6269202464699193298" datatype="html">
<source>Arabic</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">166</context>
<context context-type="linenumber">167</context>
</context-group>
</trans-unit>
<trans-unit id="3098941349689899577" datatype="html">
<source>Belarusian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">172</context>
<context context-type="linenumber">173</context>
</context-group>
</trans-unit>
<trans-unit id="1001043467371963032" datatype="html">
<source>Catalan</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">178</context>
<context context-type="linenumber">179</context>
</context-group>
</trans-unit>
<trans-unit id="2719780722934172508" datatype="html">
<source>Czech</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">184</context>
<context context-type="linenumber">185</context>
</context-group>
</trans-unit>
<trans-unit id="2924289692679201020" datatype="html">
<source>Danish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">190</context>
<context context-type="linenumber">191</context>
</context-group>
</trans-unit>
<trans-unit id="1858110241312746425" datatype="html">
<source>German</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">196</context>
<context context-type="linenumber">197</context>
</context-group>
</trans-unit>
<trans-unit id="7067741492320440272" datatype="html">
<source>Greek</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">202</context>
<context context-type="linenumber">203</context>
</context-group>
</trans-unit>
<trans-unit id="6987083569809053351" datatype="html">
<source>English (GB)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">208</context>
<context context-type="linenumber">209</context>
</context-group>
</trans-unit>
<trans-unit id="5190825892106392539" datatype="html">
<source>Spanish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">214</context>
<context context-type="linenumber">215</context>
</context-group>
</trans-unit>
<trans-unit id="861663369293303028" datatype="html">
<source>Finnish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">220</context>
<context context-type="linenumber">221</context>
</context-group>
</trans-unit>
<trans-unit id="7633754075223722162" datatype="html">
<source>French</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">226</context>
<context context-type="linenumber">227</context>
</context-group>
</trans-unit>
<trans-unit id="2935232983274991580" datatype="html">
<source>Italian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">232</context>
<context context-type="linenumber">233</context>
</context-group>
</trans-unit>
<trans-unit id="1334425850005897370" datatype="html">
<source>Luxembourgish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">238</context>
<context context-type="linenumber">239</context>
</context-group>
</trans-unit>
<trans-unit id="3071065188816255493" datatype="html">
<source>Dutch</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">244</context>
<context context-type="linenumber">245</context>
</context-group>
</trans-unit>
<trans-unit id="8069284467804715623" datatype="html">
<source>Norwegian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">250</context>
<context context-type="linenumber">251</context>
</context-group>
</trans-unit>
<trans-unit id="792060551707690640" datatype="html">
<source>Polish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">256</context>
<context context-type="linenumber">257</context>
</context-group>
</trans-unit>
<trans-unit id="9184513005098760425" datatype="html">
<source>Portuguese (Brazil)</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">262</context>
<context context-type="linenumber">263</context>
</context-group>
</trans-unit>
<trans-unit id="153799456510623899" datatype="html">
<source>Portuguese</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">268</context>
<context context-type="linenumber">269</context>
</context-group>
</trans-unit>
<trans-unit id="8118856427047826368" datatype="html">
<source>Romanian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">274</context>
<context context-type="linenumber">275</context>
</context-group>
</trans-unit>
<trans-unit id="7137419789978325708" datatype="html">
<source>Russian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">280</context>
<context context-type="linenumber">281</context>
</context-group>
</trans-unit>
<trans-unit id="9102963095355753902" datatype="html">
<source>Slovak</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">286</context>
<context context-type="linenumber">287</context>
</context-group>
</trans-unit>
<trans-unit id="4287008301409320881" datatype="html">
<source>Slovenian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">292</context>
<context context-type="linenumber">293</context>
</context-group>
</trans-unit>
<trans-unit id="8608389829607915090" datatype="html">
<source>Serbian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">298</context>
<context context-type="linenumber">299</context>
</context-group>
</trans-unit>
<trans-unit id="499386805970351976" datatype="html">
<source>Swedish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">304</context>
<context context-type="linenumber">305</context>
</context-group>
</trans-unit>
<trans-unit id="5682359291233237791" datatype="html">
<source>Turkish</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">310</context>
<context context-type="linenumber">311</context>
</context-group>
</trans-unit>
<trans-unit id="3578644052206125685" datatype="html">
<source>Ukrainian</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">316</context>
<context context-type="linenumber">317</context>
</context-group>
</trans-unit>
<trans-unit id="4689443708886954687" datatype="html">
<source>Chinese Simplified</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">322</context>
<context context-type="linenumber">323</context>
</context-group>
</trans-unit>
<trans-unit id="4912706592792948707" datatype="html">
<source>ISO 8601</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">339</context>
<context context-type="linenumber">340</context>
</context-group>
</trans-unit>
<trans-unit id="313643372755303297" datatype="html">
<source>Successfully completed one-time migratration of settings to the database!</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">458</context>
<context context-type="linenumber">459</context>
</context-group>
</trans-unit>
<trans-unit id="5558341108007064934" datatype="html">
<source>Unable to migrate settings to the database, please try saving manually.</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/services/settings.service.ts</context>
<context context-type="linenumber">459</context>
<context context-type="linenumber">460</context>
</context-group>
</trans-unit>
<trans-unit id="1168781785897678748" datatype="html">

View File

@ -180,11 +180,23 @@ describe('SettingsComponent', () => {
activatedRoute.snapshot.fragment = '#notifications'
const scrollSpy = jest.spyOn(viewportScroller, 'scrollToAnchor')
component.ngOnInit()
expect(component.activeNavID).toEqual(3) // Users & Groups
expect(component.activeNavID).toEqual(3) // Notifications
component.ngAfterViewInit()
expect(scrollSpy).toHaveBeenCalledWith('#notifications')
})
it('should enable organizing of sidebar saved views even on direct navigation', () => {
completeSetup()
jest
.spyOn(activatedRoute, 'paramMap', 'get')
.mockReturnValue(of(convertToParamMap({ section: 'savedviews' })))
activatedRoute.snapshot.fragment = '#savedviews'
component.ngOnInit()
expect(component.activeNavID).toEqual(4) // Saved Views
component.ngAfterViewInit()
expect(settingsService.organizingSidebarSavedViews).toBeTruthy()
})
it('should support save saved views, show error', () => {
completeSetup()

View File

@ -194,6 +194,9 @@ export class SettingsComponent
if (navIDKey) {
this.activeNavID = SettingsNavIDs[navIDKey]
}
if (this.activeNavID === SettingsNavIDs.SavedViews) {
this.settings.organizingSidebarSavedViews = true
}
}
})
}
@ -275,11 +278,15 @@ export class SettingsComponent
this.router
.navigate(['settings', foundNavIDkey.toLowerCase()])
.then((navigated) => {
this.settings.organizingSidebarSavedViews = false
if (!navigated && this.isDirty) {
this.activeNavID = navChangeEvent.activeId
} else if (navigated && this.isDirty) {
this.initialize()
}
if (this.activeNavID === SettingsNavIDs.SavedViews) {
this.settings.organizingSidebarSavedViews = true
}
})
}
@ -352,6 +359,7 @@ export class SettingsComponent
ngOnDestroy() {
if (this.isDirty) this.settings.updateAppearanceSettings() // in case user changed appearance but didnt save
this.storeSub && this.storeSub.unsubscribe()
this.settings.organizingSidebarSavedViews = false
}
deleteSavedView(savedView: PaperlessSavedView) {

View File

@ -87,17 +87,28 @@
</li>
</ul>
<ng-container *pngxIfPermissions="{ action: PermissionAction.View, type: PermissionType.SavedView }">
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='savedViewService.loading || savedViewService.sidebarViews.length > 0'>
<h6 class="sidebar-heading px-3 mt-3 mb-1 text-muted" *ngIf='savedViewService.loading || sidebarViews?.length > 0'>
<span i18n>Saved views</span>
<div *ngIf="savedViewService.loading" class="spinner-border spinner-border-sm fw-normal ms-2" role="status"></div>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item w-100" *ngFor="let view of savedViewService.sidebarViews">
<ul class="nav flex-column mb-2" cdkDropList (cdkDropListDropped)="onDrop($event)">
<li class="nav-item w-100" *ngFor="let view of sidebarViews"
cdkDrag
[cdkDragDisabled]="!settingsService.organizingSidebarSavedViews"
cdkDragPreviewContainer="parent"
cdkDragPreviewClass="navItemDrag"
(cdkDragStarted)="onDragStart($event)"
(cdkDragEnded)="onDragEnd($event)">
<a class="nav-link" [class.text-truncate]="!slimSidebarEnabled" routerLink="view/{{view.id}}" routerLinkActive="active" (click)="closeMenu()" [ngbPopover]="view.name" [disablePopover]="!slimSidebarEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim">
<svg class="sidebaricon" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#funnel"/>
</svg><span>&nbsp;{{view.name}}</span>
</a>
<div *ngIf="settingsService.organizingSidebarSavedViews" class="position-absolute end-0 top-0 px-3 py-2" [class.me-n3]="slimSidebarEnabled" cdkDragHandle>
<svg class="sidebaricon text-muted" fill="currentColor">
<use xlink:href="assets/bootstrap-icons.svg#grip-vertical"/>
</svg>
</div>
</li>
</ul>
</ng-container>

View File

@ -169,6 +169,7 @@ main {
.nav-item {
position: relative;
list-style-type: none;
&:hover .close {
display: block;
@ -310,3 +311,11 @@ main {
opacity: 100%;
}
}
.nav-item .position-absolute {
cursor: move;
}
::ng-deep .navItemDrag .position-absolute svg {
display: none;
}

View File

@ -19,7 +19,7 @@ import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
import { RemoteVersionService } from 'src/app/services/rest/remote-version.service'
import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive'
import { FormsModule, ReactiveFormsModule } from '@angular/forms'
import { of } from 'rxjs'
import { of, throwError } from 'rxjs'
import { ToastService } from 'src/app/services/toast.service'
import { environment } from 'src/environments/environment'
import { OpenDocumentsService } from 'src/app/services/open-documents.service'
@ -30,7 +30,47 @@ import { DocumentListViewService } from 'src/app/services/document-list-view.ser
import { FILTER_FULLTEXT_QUERY } from 'src/app/data/filter-rule-type'
import { routes } from 'src/app/app-routing.module'
import { PermissionsGuard } from 'src/app/guards/permissions.guard'
import { CdkDragDrop } from '@angular/cdk/drag-drop'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
const saved_views = [
{
name: 'Saved View 0',
id: 0,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 1',
id: 1,
show_on_dashboard: false,
show_in_sidebar: false,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 2',
id: 2,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 3',
id: 3,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
]
const document = { id: 2, title: 'Hello world' }
describe('AppFrameComponent', () => {
@ -60,7 +100,19 @@ describe('AppFrameComponent', () => {
],
providers: [
SettingsService,
SavedViewService,
{
provide: SavedViewService,
useValue: {
initialize: () => {},
listAll: () =>
of({
all: [saved_views.map((v) => v.id)],
count: saved_views.length,
results: saved_views,
}),
sidebarViews: saved_views.filter((v) => v.show_in_sidebar),
},
},
PermissionsService,
RemoteVersionService,
IfPermissionsDirective,
@ -269,4 +321,45 @@ describe('AppFrameComponent', () => {
},
])
})
it('should disable global dropzone on start drag + drop, re-enable after', () => {
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
component.onDragStart(null)
expect(settingsService.globalDropzoneEnabled).toBeFalsy()
component.onDragEnd(null)
expect(settingsService.globalDropzoneEnabled).toBeTruthy()
})
it('should update saved view sorting on drag + drop, show info', () => {
const settingsSpy = jest.spyOn(settingsService, 'updateSidebarViewsSort')
const toastSpy = jest.spyOn(toastService, 'showInfo')
jest.spyOn(settingsService, 'storeSettings').mockReturnValue(of(true))
component.onDrop({ previousIndex: 0, currentIndex: 1 } as CdkDragDrop<
PaperlessSavedView[]
>)
expect(settingsSpy).toHaveBeenCalledWith([
saved_views[2],
saved_views[0],
saved_views[3],
])
expect(toastSpy).toHaveBeenCalled()
})
it('should update saved view sorting on drag + drop, show error', () => {
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return []
})
fixture.destroy()
fixture = TestBed.createComponent(AppFrameComponent)
component = fixture.componentInstance
fixture.detectChanges()
const toastSpy = jest.spyOn(toastService, 'showError')
jest
.spyOn(settingsService, 'storeSettings')
.mockReturnValue(throwError(() => new Error('unable to save')))
component.onDrop({ previousIndex: 0, currentIndex: 2 } as CdkDragDrop<
PaperlessSavedView[]
>)
expect(toastSpy).toHaveBeenCalled()
})
})

View File

@ -32,6 +32,13 @@ import {
PermissionsService,
PermissionType,
} from 'src/app/services/permissions.service'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import {
CdkDragStart,
CdkDragEnd,
CdkDragDrop,
moveItemInArray,
} from '@angular/cdk/drag-drop'
@Component({
selector: 'pngx-app-frame',
@ -42,6 +49,17 @@ export class AppFrameComponent
extends ComponentWithPermissions
implements OnInit, ComponentCanDeactivate
{
versionString = `${environment.appTitle} ${environment.version}`
appRemoteVersion: AppRemoteVersion
isMenuCollapsed: boolean = true
slimSidebarAnimating: boolean = false
searchField = new FormControl('')
sidebarViews: PaperlessSavedView[]
constructor(
public router: Router,
private activatedRoute: ActivatedRoute,
@ -63,7 +81,7 @@ export class AppFrameComponent
PermissionType.SavedView
)
) {
savedViewService.initialize()
this.savedViewService.initialize()
}
}
@ -72,15 +90,12 @@ export class AppFrameComponent
this.checkForUpdates()
}
this.tasksService.reload()
this.savedViewService.listAll().subscribe(() => {
this.sidebarViews = this.savedViewService.sidebarViews
})
}
versionString = `${environment.appTitle} ${environment.version}`
appRemoteVersion: AppRemoteVersion
isMenuCollapsed: boolean = true
slimSidebarAnimating: boolean = false
toggleSlimSidebar(): void {
this.slimSidebarAnimating = true
this.slimSidebarEnabled = !this.slimSidebarEnabled
@ -121,8 +136,6 @@ export class AppFrameComponent
return !this.openDocumentsService.hasDirty()
}
searchField = new FormControl('')
get searchFieldEmpty(): boolean {
return this.searchField.value.trim().length == 0
}
@ -218,6 +231,27 @@ export class AppFrameComponent
})
}
onDragStart(event: CdkDragStart) {
this.settingsService.globalDropzoneEnabled = false
}
onDragEnd(event: CdkDragEnd) {
this.settingsService.globalDropzoneEnabled = true
}
onDrop(event: CdkDragDrop<PaperlessSavedView[]>) {
moveItemInArray(this.sidebarViews, event.previousIndex, event.currentIndex)
this.settingsService.updateSidebarViewsSort(this.sidebarViews).subscribe({
next: () => {
this.toastService.showInfo($localize`Sidebar views updated`)
},
error: (e) => {
this.toastService.showError($localize`Error updating sidebar views`, e)
},
})
}
private checkForUpdates() {
this.remoteVersionService
.checkForUpdates()

View File

@ -1,19 +1,3 @@
.col-sidebar .row {
top: 3.5rem;
}
:host ::ng-deep {
.cdk-drag-placeholder {
opacity: .5;
}
/* Animate items as they're being sorted. */
.cdk-drop-list-dragging .cdk-drag {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Animate an item that has been dropped. */
.cdk-drag-animating {
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1);
}
}

View File

@ -29,22 +29,7 @@ export class DashboardComponent extends ComponentWithPermissions {
super()
this.savedViewService.listAll().subscribe(() => {
const sorted: number[] = this.settingsService.get(
SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER
)
this.dashboardViews =
sorted?.length > 0
? sorted
.map((id) =>
this.savedViewService.dashboardViews.find((v) => v.id === id)
)
.concat(
this.savedViewService.dashboardViews.filter(
(v) => !sorted.includes(v.id)
)
)
.filter((v) => v)
: [...this.savedViewService.dashboardViews]
this.dashboardViews = this.savedViewService.dashboardViews
})
}

View File

@ -17,7 +17,7 @@
<div class="d-flex justify-content-between align-items-center my-2">
<div class="progress flex-grow-1">
<div *ngFor="let filetype of statistics?.document_file_type_counts; let i = index; let last = last"
class="progress-bar bg-primary text-primary-contrast"
class="progress-bar bg-primary"
role="progressbar"
[ngbPopover]="getFileTypeName(filetype)"
i18n-ngbPopover

View File

@ -1,4 +1,4 @@
<ngb-alert class="pe-3" type="primary" [dismissible]="true" (closed)="dismiss.emit(true)">
<ngb-alert class="pe-3 text-primary-contrast" type="primary" [dismissible]="true" (closed)="dismiss.emit(true)">
<h4 class="alert-heading"><ng-container i18n>Paperless-ngx is running!</ng-container> 🎉</h4>
<p i18n>You're ready to start uploading documents! Explore the various features of this web app on your own, or start a quick tour using the button below.</p>
<p i18n>More detail on how to use and configure Paperless-ngx is always available in the <a href="https://docs.paperless-ngx.com" target="_blank">documentation</a>.</p>

View File

@ -43,6 +43,8 @@ export const SETTINGS_KEYS = {
'general-settings:saved-views:warn-on-unsaved-change',
DASHBOARD_VIEWS_SORT_ORDER:
'general-settings:saved-views:dashboard-views-sort-order',
SIDEBAR_VIEWS_SORT_ORDER:
'general-settings:saved-views:sidebar-views-sort-order',
TOUR_COMPLETE: 'general-settings:tour-complete',
DEFAULT_PERMS_OWNER: 'general-settings:permissions:default-owner',
DEFAULT_PERMS_VIEW_USERS: 'general-settings:permissions:default-view-users',
@ -187,4 +189,9 @@ export const SETTINGS: PaperlessUiSetting[] = [
type: 'array',
default: [],
},
{
key: SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER,
type: 'array',
default: [],
},
]

View File

@ -4,6 +4,8 @@ import { TestBed } from '@angular/core/testing'
import { environment } from 'src/environments/environment'
import { commonAbstractPaperlessServiceTests } from './abstract-paperless-service.spec'
import { SavedViewService } from './saved-view.service'
import { SettingsService } from '../settings.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
let httpTestingController: HttpTestingController
let service: SavedViewService
@ -22,8 +24,8 @@ const saved_views = [
{
name: 'Saved View 2',
id: 2,
show_on_dashboard: false,
show_in_sidebar: false,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
@ -32,6 +34,15 @@ const saved_views = [
name: 'Saved View 3',
id: 3,
show_on_dashboard: true,
show_in_sidebar: true,
sort_field: 'name',
sort_reverse: true,
filter_rules: [],
},
{
name: 'Saved View 4',
id: 4,
show_on_dashboard: false,
show_in_sidebar: false,
sort_field: 'name',
sort_reverse: true,
@ -43,6 +54,8 @@ const saved_views = [
commonAbstractPaperlessServiceTests(endpoint, SavedViewService)
describe(`Additional service tests for SavedViewService`, () => {
let settingsService
it('should retrieve saved views and sort them', () => {
service.initialize()
const req = httpTestingController.expectOne(
@ -51,9 +64,9 @@ describe(`Additional service tests for SavedViewService`, () => {
req.flush({
results: saved_views,
})
expect(service.allViews).toHaveLength(3)
expect(service.dashboardViews).toHaveLength(2)
expect(service.sidebarViews).toHaveLength(1)
expect(service.allViews).toHaveLength(4)
expect(service.dashboardViews).toHaveLength(3)
expect(service.sidebarViews).toHaveLength(3)
})
it('should support patchMany', () => {
@ -67,11 +80,36 @@ describe(`Additional service tests for SavedViewService`, () => {
})
})
it('should sort dashboard views', () => {
service['savedViews'] = saved_views
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [3, 1, 2]
})
expect(service.dashboardViews).toEqual([
saved_views[2],
saved_views[0],
saved_views[1],
])
})
it('should sort sidebar views', () => {
service['savedViews'] = saved_views
jest.spyOn(settingsService, 'get').mockImplementation((key) => {
if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return [3, 1, 2]
})
expect(service.sidebarViews).toEqual([
saved_views[2],
saved_views[0],
saved_views[1],
])
})
beforeEach(() => {
// Dont need to setup again
httpTestingController = TestBed.inject(HttpTestingController)
service = TestBed.inject(SavedViewService)
settingsService = TestBed.inject(SettingsService)
})
afterEach(() => {

View File

@ -5,6 +5,8 @@ import { tap } from 'rxjs/operators'
import { PaperlessSavedView } from 'src/app/data/paperless-saved-view'
import { PermissionsService } from '../permissions.service'
import { AbstractPaperlessService } from './abstract-paperless-service'
import { SettingsService } from '../settings.service'
import { SETTINGS_KEYS } from 'src/app/data/paperless-uisettings'
@Injectable({
providedIn: 'root',
@ -12,7 +14,11 @@ import { AbstractPaperlessService } from './abstract-paperless-service'
export class SavedViewService extends AbstractPaperlessService<PaperlessSavedView> {
loading: boolean
constructor(http: HttpClient, permissionService: PermissionsService) {
constructor(
http: HttpClient,
permissionService: PermissionsService,
private settingsService: SettingsService
) {
super(http, 'saved_views')
}
@ -25,6 +31,7 @@ export class SavedViewService extends AbstractPaperlessService<PaperlessSavedVie
this.listAll().subscribe((r) => {
this.savedViews = r.results
this.loading = false
this.settingsService.dashboardIsEmpty = this.dashboardViews.length === 0
})
}
@ -34,12 +41,34 @@ export class SavedViewService extends AbstractPaperlessService<PaperlessSavedVie
return this.savedViews
}
get sidebarViews() {
return this.savedViews.filter((v) => v.show_in_sidebar)
get sidebarViews(): PaperlessSavedView[] {
const sidebarViews = this.savedViews.filter((v) => v.show_in_sidebar)
const sorted: number[] = this.settingsService.get(
SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER
)
return sorted?.length > 0
? sorted
.map((id) => sidebarViews.find((v) => v.id === id))
.concat(sidebarViews.filter((v) => !sorted.includes(v.id)))
.filter((v) => v)
: [...sidebarViews]
}
get dashboardViews() {
return this.savedViews.filter((v) => v.show_on_dashboard)
get dashboardViews(): PaperlessSavedView[] {
const dashboardViews = this.savedViews.filter((v) => v.show_on_dashboard)
const sorted: number[] = this.settingsService.get(
SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER
)
return sorted?.length > 0
? sorted
.map((id) => dashboardViews.find((v) => v.id === id))
.concat(dashboardViews.filter((v) => !sorted.includes(v.id)))
.filter((v) => v)
: [...dashboardViews]
}
create(o: PaperlessSavedView) {

View File

@ -292,6 +292,14 @@ describe('SettingsService', () => {
SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER,
[1, 4]
)
settingsService.updateSidebarViewsSort([
{ id: 1 } as PaperlessSavedView,
{ id: 4 } as PaperlessSavedView,
])
expect(setSpy).toHaveBeenCalledWith(
SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER,
[1, 4]
)
httpTestingController
.expectOne(`${environment.apiBaseUrl}ui_settings/`)
.flush(ui_settings)

View File

@ -24,7 +24,6 @@ import {
} from '../data/paperless-uisettings'
import { PaperlessUser } from '../data/paperless-user'
import { PermissionsService } from './permissions.service'
import { SavedViewService } from './rest/saved-view.service'
import { ToastService } from './toast.service'
import { PaperlessSavedView } from '../data/paperless-saved-view'
@ -55,8 +54,11 @@ export class SettingsService {
return this._renderer
}
public dashboardIsEmpty: boolean = false
public globalDropzoneEnabled: boolean = true
public globalDropzoneActive: boolean = false
public organizingSidebarSavedViews: boolean = false
constructor(
rendererFactory: RendererFactory2,
@ -66,7 +68,6 @@ export class SettingsService {
@Inject(LOCALE_ID) private localeId: string,
protected http: HttpClient,
private toastService: ToastService,
private savedViewService: SavedViewService,
private permissionsService: PermissionsService
) {
this._renderer = rendererFactory.createRenderer(null, null)
@ -515,11 +516,7 @@ export class SettingsService {
}
offerTour(): boolean {
return (
!this.savedViewService.loading &&
this.savedViewService.dashboardViews.length == 0 &&
!this.get(SETTINGS_KEYS.TOUR_COMPLETE)
)
return this.dashboardIsEmpty && !this.get(SETTINGS_KEYS.TOUR_COMPLETE)
}
completeTour() {
@ -544,4 +541,11 @@ export class SettingsService {
])
return this.storeSettings()
}
updateSidebarViewsSort(sidebarViews: PaperlessSavedView[]): Observable<any> {
this.set(SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER, [
...new Set(sidebarViews.map((v) => v.id)),
])
return this.storeSettings()
}
}

View File

@ -642,3 +642,17 @@ code {
.me-1px {
margin-right: 1px !important;
}
.cdk-drag-placeholder {
opacity: .5;
}
/* Animate items as they're being sorted. */
.cdk-drop-list-dragging .cdk-drag {
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
}
/* Animate an item that has been dropped. */
.cdk-drag-animating {
transition: transform 300ms cubic-bezier(0, 0, 0.2, 1);
}

View File

@ -226,7 +226,7 @@ $form-check-radio-checked-bg-image-dark: url("data:image/svg+xml,<svg xmlns='htt
}
.alert-primary {
--bs-alert-color: var(--pngx-primary-text-contrast);
--bs-alert-color: var(--bs-primary);
--bs-alert-bg: var(--pngx-primary-darken-27);
--bs-alert-border-color: var(--pngx-bg-darker);
}