Merge branch 'release/v1.2.0'
This commit is contained in:
commit
73471b1a52
14
.travis.yml
14
.travis.yml
|
@ -48,12 +48,12 @@ matrix:
|
|||
- env: TEST_SUITE=jest
|
||||
|
||||
script:
|
||||
- travis_retry npm run travis -- "$TEST_SUITE"
|
||||
- NODE_PENDING_JOB_WAIT=1000 travis_retry npm run travis -- "$TEST_SUITE"
|
||||
|
||||
after_failure:
|
||||
- cat test1/logs/all-logs.log
|
||||
- cat test2/logs/all-logs.log
|
||||
- cat test3/logs/all-logs.log
|
||||
- cat test4/logs/all-logs.log
|
||||
- cat test5/logs/all-logs.log
|
||||
- cat test6/logs/all-logs.log
|
||||
- cat test1/logs/peertube.log
|
||||
- cat test2/logs/peertube.log
|
||||
- cat test3/logs/peertube.log
|
||||
- cat test4/logs/peertube.log
|
||||
- cat test5/logs/peertube.log
|
||||
- cat test6/logs/peertube.log
|
||||
|
|
93
CHANGELOG.md
93
CHANGELOG.md
|
@ -1,9 +1,101 @@
|
|||
# Changelog
|
||||
|
||||
## v1.2.0
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **Docker:** `PEERTUBE_TRUST_PROXY` env variable is now an array ([LecygneNoir](https://github.com/LecygneNoir))
|
||||
* **Docker:** Check you have all the storage fields in your `/config/production.yaml` file: https://github.com/Chocobozzz/PeerTube/blob/develop/support/docker/production/config/production.yaml#L34
|
||||
* **nginx:** Add redundancy endpoint in static file. **You should add it in your nginx configuration: https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/production.md#nginx**
|
||||
* **nginx:** Add socket io endpoint. **You should add it in your nginx configuration: https://github.com/Chocobozzz/PeerTube/blob/develop/support/doc/production.md#nginx**
|
||||
* Moderators can manage users now (add/delete/update/block)
|
||||
* Add `tmp` and `redundancy` directories in configuration file. **You should configure them in your production.yaml**
|
||||
|
||||
### Maintenance
|
||||
|
||||
* Check free storage before upgrading in upgrade script ([@Nutomic](https://github.com/nutomic))
|
||||
* Explain that PeerTube must be stopped in prune storage script
|
||||
* Add some security directives in the systemd unit configuration file ([@rigelk](https://github.com/rigelk) & [@mkoppmann](https://github.com/mkoppmann))
|
||||
* Update FreeBSD startup script ([@gegeweb](https://github.com/gegeweb))
|
||||
|
||||
### Docker
|
||||
|
||||
* Patch docker entrypoint to speed up the chown at startup ([LecygneNoir](https://github.com/LecygneNoir))
|
||||
|
||||
### Features
|
||||
|
||||
* Add Russian, Polish and Italian languages
|
||||
* Add user notifications:
|
||||
* Notification types:
|
||||
* Comment on my video
|
||||
* New video from my subscriptions
|
||||
* New video abuses (for moderators)
|
||||
* Blacklist/Unblacklist on my video
|
||||
* Video import finished (error or success)
|
||||
* Pending video published (after transcoding or a scheduled update)
|
||||
* My account or one of my channel has a new follower
|
||||
* Someone (except muted accounts) mentioned me in comments
|
||||
* A user registered on the instance (for moderators)
|
||||
* Notification actions:
|
||||
* Add a web notification
|
||||
* Send an english email
|
||||
* Add contact form in about page (**enabled by default**)
|
||||
* Add ability to unfederate a local video in blacklist modal (**checkbox checked by default**)
|
||||
* Support additional video extensions if transcoding is enabled (**enabled by default**)
|
||||
* Redirect to the last url on login
|
||||
* Add ability to automatically set the video caption in URL. Example: https://peertube2.cpy.re/videos/watch/9c9de5e8-0a1e-484a-b099-e80766180a6d?subtitle=ru
|
||||
* Automatically enable the last selected caption when watching a video
|
||||
* Add ability to disable, clear and list user videos history
|
||||
* Add a button to help to translate peertube
|
||||
* Add text in the report modal to explain to whom the report will be sent
|
||||
* Open my account menu entries on hover
|
||||
* Explain what features are enabled on the instance in the about page
|
||||
* Add an error message in the forgot password modal if the instance email system is not configured
|
||||
* Add sitemap
|
||||
* Add well known url to change password ([@rigelk](https://github.com/rigelk))
|
||||
* Remove 8GB video upload limit on client side. There may still be such limit depending on the reverse proxy configuration ([@scanlime](https://github.com/scanlime))
|
||||
* Add CSP ([@rigelk](https://github.com/rigelk) & [@Nutomic](https://github.com/nutomic))
|
||||
* Update title and description HTML tags when rendering video HTML page
|
||||
* Add webfinger support for remote follows ([@acid-chicken](https://github.com/acid-chicken))
|
||||
* Add tooltip to explain how the trending algorithm works ([@auberanger](https://github.com/auberanger))
|
||||
* Warn users when they want to delete a channel because they will not be able to create another channel with the same name
|
||||
* Warn users when they leave the video upload/update (on page refresh/tab close)
|
||||
* Set max user name, user display name, channel name and channel display name lengths to 50 characters ([@McFlat](https://github.com/mcflat))
|
||||
* Increase video abuse length to 3000 characters
|
||||
* Add totalLocalVideoFilesSize in the stats endpoint
|
||||
|
||||
## Bug fixes
|
||||
|
||||
* Fix the addition of captions to a video
|
||||
* Fix federation of some videos
|
||||
* Fix NSFW blur on search
|
||||
* Add error message when trying to upload .ass subtitles
|
||||
* Fix default homepage in the progressive web application
|
||||
* Don't crash on queue error
|
||||
* Fix EXDEV errors if you have multiple mount points
|
||||
* Fix broken audio in transcoding with some videos
|
||||
* Fix crash on getVideoFileStream issue
|
||||
* Fix followers search
|
||||
* Remove trailing `/` in CLI import script ([@HesioZ](https://github.com/HesioZ/))
|
||||
* Use origin video url in canonical tag
|
||||
* Fix captions in HTTP fallback
|
||||
* Automatically refresh remote actors to fix deleted remote actors that are still displayed on some instances
|
||||
* Add missing translations in video embed page
|
||||
* Fix some styling issues in dark mode
|
||||
* Fix transcoding issues with some videos
|
||||
* Fix Mac OS mkv/avi upload
|
||||
* Fix menu overflow on mobile
|
||||
* Fix ownership button icons ([@joshmorel](https://github.com/joshmorel))
|
||||
|
||||
|
||||
## v1.1.0
|
||||
|
||||
***Since v1.0.1***
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **Docker:** `PEERTUBE_TRUST_PROXY` env variable is now an array ([LecygneNoir](https://github.com/LecygneNoir))
|
||||
|
||||
### Maintenance
|
||||
|
||||
* Improve REST API documentation: https://docs.joinpeertube.org/api.html ([@rigelk](https://github.com/rigelk))
|
||||
|
@ -26,7 +118,6 @@
|
|||
* Add postfix image
|
||||
* Redirect HTTP -> HTTPS
|
||||
* Disable Træfik web UI
|
||||
* Add ability to set an array in `PEERTUBE_TRUST_PROXY` ([LecygneNoir](https://github.com/LecygneNoir))
|
||||
|
||||
### Features
|
||||
|
||||
|
|
20
CREDITS.md
20
CREDITS.md
|
@ -6,25 +6,27 @@
|
|||
* [Nutomic](https://github.com/Nutomic)
|
||||
* [Jorropo](https://github.com/Jorropo)
|
||||
* [BO41](https://github.com/BO41)
|
||||
* [joshmorel](https://github.com/joshmorel)
|
||||
* [buoyantair](https://github.com/buoyantair)
|
||||
* [bnjbvr](https://github.com/bnjbvr)
|
||||
* [DavidLibeau](https://github.com/DavidLibeau)
|
||||
* [jankeromnes](https://github.com/jankeromnes)
|
||||
* [joshmorel](https://github.com/joshmorel)
|
||||
* [JohnXLivingston](https://github.com/JohnXLivingston)
|
||||
* [kaiyou](https://github.com/kaiyou)
|
||||
* [McFlat](https://github.com/McFlat)
|
||||
* [DimitriGilbert](https://github.com/DimitriGilbert)
|
||||
* [floSoX](https://github.com/floSoX)
|
||||
* [Green-Star](https://github.com/Green-Star)
|
||||
* [thomaskuntzz](https://github.com/thomaskuntzz)
|
||||
* [rezonant](https://github.com/rezonant)
|
||||
* [ldidry](https://github.com/ldidry)
|
||||
* [McFlat](https://github.com/McFlat)
|
||||
* [okhin](https://github.com/okhin)
|
||||
* [daftaupe](https://github.com/daftaupe)
|
||||
* [thomaskuntzz](https://github.com/thomaskuntzz)
|
||||
* [LecygneNoir](https://github.com/LecygneNoir)
|
||||
* [fflorent](https://github.com/fflorent)
|
||||
* [dedesite](https://github.com/dedesite)
|
||||
* [Nautigsam](https://github.com/Nautigsam)
|
||||
* [scanlime](https://github.com/scanlime)
|
||||
* [tcitworld](https://github.com/tcitworld)
|
||||
* [am97](https://github.com/am97)
|
||||
* [dadall](https://github.com/dadall)
|
||||
|
@ -35,7 +37,6 @@
|
|||
* [jocelynj](https://github.com/jocelynj)
|
||||
* [lucas-dclrcq](https://github.com/lucas-dclrcq)
|
||||
* [lucaspontoexe](https://github.com/lucaspontoexe)
|
||||
* [scanlime](https://github.com/scanlime)
|
||||
* [flyingrub](https://github.com/flyingrub)
|
||||
* [SerCom-KC](https://github.com/SerCom-KC)
|
||||
* [valvin1](https://github.com/valvin1)
|
||||
|
@ -43,6 +44,7 @@
|
|||
* [sticmac](https://github.com/sticmac)
|
||||
* [barbeque](https://github.com/barbeque)
|
||||
* [luzpaz](https://github.com/luzpaz)
|
||||
* [acid-chicken](https://github.com/acid-chicken)
|
||||
* [louistio](https://github.com/louistio)
|
||||
* [qsypoq](https://github.com/qsypoq)
|
||||
* [daker](https://github.com/daker)
|
||||
|
@ -65,6 +67,7 @@
|
|||
* [grizio](https://github.com/grizio)
|
||||
* [Glandos](https://github.com/Glandos)
|
||||
* [lanodan](https://github.com/lanodan)
|
||||
* [HesioZ](https://github.com/HesioZ)
|
||||
* [jagannathBhat](https://github.com/jagannathBhat)
|
||||
* [jlebras](https://github.com/jlebras)
|
||||
* [alcalyn](https://github.com/alcalyn)
|
||||
|
@ -73,7 +76,9 @@
|
|||
* [zapashcanon](https://github.com/zapashcanon)
|
||||
* [mart-e](https://github.com/mart-e)
|
||||
* [0mp](https://github.com/0mp)
|
||||
* [mkoppmann](https://github.com/mkoppmann)
|
||||
* [1000i100](https://github.com/1000i100)
|
||||
* [roipoussiere](https://github.com/roipoussiere)
|
||||
* [zeograd](https://github.com/zeograd)
|
||||
* [PhieF](https://github.com/PhieF)
|
||||
* [Quenty31](https://github.com/Quenty31)
|
||||
|
@ -125,11 +130,14 @@
|
|||
* [h3zjp](https://trad.framasoft.org/zanata/profile/view/h3zjp)
|
||||
* [jfblanc](https://trad.framasoft.org/zanata/profile/view/jfblanc)
|
||||
* [jhertel](https://trad.framasoft.org/zanata/profile/view/jhertel)
|
||||
* [jmf](https://trad.framasoft.org/zanata/profile/view/jmf)
|
||||
* [jorropo](https://trad.framasoft.org/zanata/profile/view/jorropo)
|
||||
* [kairozen](https://trad.framasoft.org/zanata/profile/view/kairozen)
|
||||
* [kedemferre](https://trad.framasoft.org/zanata/profile/view/kedemferre)
|
||||
* [kousha](https://trad.framasoft.org/zanata/profile/view/kousha)
|
||||
* [krkk](https://trad.framasoft.org/zanata/profile/view/krkk)
|
||||
* [landrok](https://trad.framasoft.org/zanata/profile/view/landrok)
|
||||
* [leeroyepold48](https://trad.framasoft.org/zanata/profile/view/leeroyepold48)
|
||||
* [m4sk1n](https://trad.framasoft.org/zanata/profile/view/m4sk1n)
|
||||
* [matograine](https://trad.framasoft.org/zanata/profile/view/matograine)
|
||||
* [medow](https://trad.framasoft.org/zanata/profile/view/medow)
|
||||
|
@ -167,6 +175,10 @@
|
|||
* [xinayder](https://trad.framasoft.org/zanata/profile/view/xinayder)
|
||||
* [xosem](https://trad.framasoft.org/zanata/profile/view/xosem)
|
||||
* [zveryok](https://trad.framasoft.org/zanata/profile/view/zveryok)
|
||||
* [aditoo](https://trad.framasoft.org/zanata/profile/view/aditoo)
|
||||
* [autom](https://trad.framasoft.org/zanata/profile/view/autom)
|
||||
* [curupira](https://trad.framasoft.org/zanata/profile/view/curupira)
|
||||
* [leeroyepold48](https://trad.framasoft.org/zanata/profile/view/leeroyepold48)
|
||||
|
||||
|
||||
# Design
|
||||
|
|
14
FAQ.md
14
FAQ.md
|
@ -13,6 +13,8 @@
|
|||
- [Will an index of all the videos of servers you follow be too large for small servers?](#will-an-index-of-all-the-videos-of-servers-you-follow-be-too-large-for-small-servers)
|
||||
- [Which container formats can I use for the videos I want to upload?](#which-container-formats-can-i-use-for-the-videos-i-want-to-upload)
|
||||
- [I want to change my domain name, how can I do that?](#i-want-to-change-my-domain-name-how-can-i-do-that)
|
||||
- [Why do we have to put our Twitter username in PeerTube configuration?](#why-do-we-have-to-put-our-twitter-username-in-peertube-configuration)
|
||||
- [How video views are calculated?](#how-video-views-are-calculated)
|
||||
- [Should I have a big server to run PeerTube?](#should-i-have-a-big-server-to-run-peertube)
|
||||
- [Can I seed videos with my classic BitTorrent client (Transmission, rTorrent...)?](#can-i-seed-videos-with-my-classic-bittorrent-client-transmission-rtorrent)
|
||||
- [Why host on GitHub and Framagit?](#why-host-on-github-and-framagit)
|
||||
|
@ -89,6 +91,18 @@ WEBM, MP4 or OGV videos.
|
|||
You can't. You'll need to reinstall an instance and reupload your videos.
|
||||
|
||||
|
||||
## Why do we have to put our Twitter username in PeerTube configuration?
|
||||
|
||||
You don't have to: we set a default value if you don't have a Twitter account.
|
||||
We need this information because Twitter requires an account for links share/videos embed on their platform.
|
||||
|
||||
|
||||
## How video views are calculated?
|
||||
|
||||
Your web browser sends a view to the server after 30 seconds of playback. Then, the IP cannot send another view in the next hour.
|
||||
Views are buffered, so don't panic if the view counter stays the same after you watched a video.
|
||||
|
||||
|
||||
## Should I have a big server to run PeerTube?
|
||||
|
||||
Not really. For instance, the demonstration server [https://peertube.cpy.re](https://peertube.cpy.re) has 2 vCore and 2GB of RAM and consumes on average:
|
||||
|
|
12
README.md
12
README.md
|
@ -16,6 +16,16 @@
|
|||
Be part of a network of multiple small federated, interoperable video hosting providers. Follow video creators and create videos. No vendor lock-in. All on a platform that is community-owned and ad-free.
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>Developed with ❤ by <a href="https://framasoft.org">Framasoft</a></strong>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://framasoft.org">
|
||||
<img width="150px" src="http://lutim.cpy.re/Prd3ci7G.png" alt="Framasoft logo"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<strong>Client</strong>
|
||||
|
||||
|
@ -123,7 +133,7 @@ You can also join the cheerful bunch that makes our community:
|
|||
|
||||
* Chat<a name="contact"></a>:
|
||||
* IRC : **[#peertube on chat.freenode.net:6697](https://kiwiirc.com/client/irc.freenode.net/#peertube)**
|
||||
* Matrix (bridged on the IRC channel) : **[#peertube:matrix.org](https://matrix.to/#/#peertube:matrix.org)**
|
||||
* Matrix (bridged on IRC and [Discord](https://discord.gg/wj8DDUT)) : **[#peertube:matrix.org](https://matrix.to/#/#peertube:matrix.org)**
|
||||
* Forum:
|
||||
* Framacolibri: [https://framacolibri.org/c/peertube](https://framacolibri.org/c/peertube)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "peertube-client",
|
||||
"version": "1.1.0",
|
||||
"version": "1.2.0",
|
||||
"private": true,
|
||||
"licence": "GPLv3",
|
||||
"author": {
|
||||
|
@ -63,26 +63,26 @@
|
|||
"setupTestFrameworkScriptFile": "<rootDir>/src/setupJest.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.10.0",
|
||||
"@angular/animations": "~7.0.2",
|
||||
"@angular/cli": "~7.0.4",
|
||||
"@angular/common": "~7.0.2",
|
||||
"@angular/compiler": "~7.0.2",
|
||||
"@angular/compiler-cli": "~7.0.2",
|
||||
"@angular/core": "~7.0.2",
|
||||
"@angular/forms": "~7.0.2",
|
||||
"@angular/http": "~7.0.2",
|
||||
"@angular/language-service": "~7.0.2",
|
||||
"@angular/platform-browser": "~7.0.2",
|
||||
"@angular/platform-browser-dynamic": "~7.0.2",
|
||||
"@angular/router": "~7.0.2",
|
||||
"@angular/service-worker": "~7.0.2",
|
||||
"@angular-devkit/build-angular": "~0.11.1",
|
||||
"@angular/animations": "~7.1.1",
|
||||
"@angular/cli": "~7.1.1",
|
||||
"@angular/common": "~7.1.1",
|
||||
"@angular/compiler": "~7.1.1",
|
||||
"@angular/compiler-cli": "~7.1.1",
|
||||
"@angular/core": "~7.1.1",
|
||||
"@angular/forms": "~7.1.1",
|
||||
"@angular/http": "~7.1.1",
|
||||
"@angular/language-service": "~7.1.1",
|
||||
"@angular/platform-browser": "~7.1.1",
|
||||
"@angular/platform-browser-dynamic": "~7.1.1",
|
||||
"@angular/router": "~7.1.1",
|
||||
"@angular/service-worker": "~7.1.1",
|
||||
"@angularclass/hmr": "^2.1.3",
|
||||
"@neos21/bootstrap3-glyphicons": "^1.0.1",
|
||||
"@ng-bootstrap/ng-bootstrap": "^4.0.0",
|
||||
"@ngx-loading-bar/core": "^2.2.0",
|
||||
"@ngx-loading-bar/http-client": "^2.2.0",
|
||||
"@ngx-loading-bar/router": "^2.2.0",
|
||||
"@ngx-loading-bar/core": "^3.0.0",
|
||||
"@ngx-loading-bar/http-client": "^3.0.0",
|
||||
"@ngx-loading-bar/router": "^3.0.0",
|
||||
"@ngx-meta/core": "^6.0.0-rc.1",
|
||||
"@ngx-translate/i18n-polyfill": "^1.0.0",
|
||||
"@types/core-js": "^2.5.0",
|
||||
|
@ -94,10 +94,10 @@
|
|||
"@types/markdown-it": "^0.0.5",
|
||||
"@types/node": "^10.9.2",
|
||||
"@types/sanitize-html": "1.18.0",
|
||||
"@types/socket.io-client": "^1.4.32",
|
||||
"@types/video.js": "^7.2.5",
|
||||
"@types/webtorrent": "^0.98.4",
|
||||
"angular2-hotkeys": "^2.1.2",
|
||||
"angular2-notifications": "^1.0.2",
|
||||
"awesome-typescript-loader": "5.2.1",
|
||||
"bootstrap": "^4.1.3",
|
||||
"buffer": "^5.1.0",
|
||||
|
@ -132,7 +132,7 @@
|
|||
"node-sass": "^4.9.3",
|
||||
"npm-font-source-sans-pro": "^1.0.2",
|
||||
"path-browserify": "^1.0.0",
|
||||
"primeng": "^6.1.2",
|
||||
"primeng": "^7.0.0",
|
||||
"process": "^0.11.10",
|
||||
"protractor": "^5.3.2",
|
||||
"purify-css": "^1.2.5",
|
||||
|
@ -142,6 +142,7 @@
|
|||
"sanitize-html": "^1.18.4",
|
||||
"sass-loader": "^7.1.0",
|
||||
"sass-resources-loader": "^2.0.0",
|
||||
"socket.io-client": "^2.2.0",
|
||||
"stream-browserify": "^2.0.1",
|
||||
"stream-http": "^3.0.0",
|
||||
"terser-webpack-plugin": "^1.1.0",
|
||||
|
@ -153,7 +154,6 @@
|
|||
"videojs-contextmenu-ui": "^5.0.0",
|
||||
"videojs-dock": "^2.0.2",
|
||||
"videojs-hotkeys": "^0.2.21",
|
||||
"webpack": "^4.17.1",
|
||||
"webpack-bundle-analyzer": "^3.0.2",
|
||||
"webpack-cli": "^3.0.8",
|
||||
"webtorrent": "https://github.com/webtorrent/webtorrent#e9b209c7970816fc29e0cc871157a4918d66001d",
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<div i18n class="about-instance-title">
|
||||
About {{ instanceName }} instance
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xl-6">
|
||||
<div class="about-instance-title">
|
||||
<div i18n>About {{ instanceName }} instance</div>
|
||||
|
||||
<div *ngIf="isContactFormEnabled" (click)="openContactModal()" i18n role="button" class="contact-admin">Contact administrator</div>
|
||||
</div>
|
||||
|
||||
<div class="short-description">
|
||||
|
@ -37,3 +41,12 @@
|
|||
User registration is currently not allowed.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12 col-xl-6">
|
||||
<label>Features found on this instance</label>
|
||||
<my-instance-features-table></my-instance-features-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<my-contact-admin-modal #contactAdminModal></my-contact-admin-modal>
|
||||
|
|
|
@ -2,11 +2,21 @@
|
|||
@import '_mixins';
|
||||
|
||||
.about-instance-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
& > div {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
& > .contact-admin {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
}
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-weight: $font-semibold;
|
||||
font-size: 20px;
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { ServerService } from '@app/core'
|
||||
import { MarkdownService } from '@app/videos/shared'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
|
||||
import { InstanceService } from '@app/shared/instance/instance.service'
|
||||
import { MarkdownService } from '@app/shared/renderer'
|
||||
|
||||
@Component({
|
||||
selector: 'my-about-instance',
|
||||
templateUrl: './about-instance.component.html',
|
||||
styleUrls: [ './about-instance.component.scss' ]
|
||||
})
|
||||
|
||||
export class AboutInstanceComponent implements OnInit {
|
||||
@ViewChild('contactAdminModal') contactAdminModal: ContactAdminModalComponent
|
||||
|
||||
shortDescription = ''
|
||||
descriptionHTML = ''
|
||||
termsHTML = ''
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private serverService: ServerService,
|
||||
private instanceService: InstanceService,
|
||||
private markdownService: MarkdownService,
|
||||
private i18n: I18n
|
||||
) {}
|
||||
|
@ -34,8 +37,12 @@ export class AboutInstanceComponent implements OnInit {
|
|||
return this.serverService.getConfig().signup.allowed
|
||||
}
|
||||
|
||||
get isContactFormEnabled () {
|
||||
return this.serverService.getConfig().email.enabled && this.serverService.getConfig().contactForm.enabled
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.serverService.getAbout()
|
||||
this.instanceService.getAbout()
|
||||
.subscribe(
|
||||
res => {
|
||||
this.shortDescription = res.instance.shortDescription
|
||||
|
@ -43,8 +50,12 @@ export class AboutInstanceComponent implements OnInit {
|
|||
this.termsHTML = this.markdownService.textMarkdownToHTML(res.instance.terms)
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error getting about from server'), err)
|
||||
() => this.notifier.error(this.i18n('Cannot get about information from server'))
|
||||
)
|
||||
}
|
||||
|
||||
openContactModal () {
|
||||
return this.contactAdminModal.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<ng-template #modal>
|
||||
<div class="modal-header">
|
||||
<h4 i18n class="modal-title">Contact {{ instanceName }} administrator</h4>
|
||||
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<form novalidate [formGroup]="form" (ngSubmit)="sendForm()">
|
||||
<div class="form-group">
|
||||
<label i18n for="fromName">Your name</label>
|
||||
<input
|
||||
type="text" id="fromName"
|
||||
formControlName="fromName" [ngClass]="{ 'input-error': formErrors.fromName }"
|
||||
>
|
||||
<div *ngIf="formErrors.fromName" class="form-error">{{ formErrors.fromName }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="fromEmail">Your email</label>
|
||||
<input
|
||||
type="text" id="fromEmail"
|
||||
formControlName="fromEmail" [ngClass]="{ 'input-error': formErrors['fromEmail'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.fromEmail" class="form-error">{{ formErrors.fromEmail }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="body">Your message</label>
|
||||
<textarea id="body" formControlName="body" [ngClass]="{ 'input-error': formErrors['body'] }">
|
||||
</textarea>
|
||||
<div *ngIf="formErrors.body" class="form-error">{{ formErrors.body }}</div>
|
||||
</div>
|
||||
|
||||
<div *ngIf="error" class="alert alert-danger">{{ error }}</div>
|
||||
|
||||
<div class="form-group inputs">
|
||||
<span i18n class="action-button action-button-cancel" (click)="hide()">
|
||||
Cancel
|
||||
</span>
|
||||
|
||||
<input
|
||||
type="submit" i18n-value value="Submit" class="action-button-submit"
|
||||
[disabled]="!form.valid"
|
||||
>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</ng-template>
|
|
@ -0,0 +1,11 @@
|
|||
@import 'variables';
|
||||
@import 'mixins';
|
||||
|
||||
input[type=text] {
|
||||
@include peertube-input-text(340px);
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@include peertube-textarea(100%, 200px);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap/modal/modal-ref'
|
||||
import { FormReactive, InstanceValidatorsService } from '@app/shared'
|
||||
import { InstanceService } from '@app/shared/instance/instance.service'
|
||||
|
||||
@Component({
|
||||
selector: 'my-contact-admin-modal',
|
||||
templateUrl: './contact-admin-modal.component.html',
|
||||
styleUrls: [ './contact-admin-modal.component.scss' ]
|
||||
})
|
||||
export class ContactAdminModalComponent extends FormReactive implements OnInit {
|
||||
@ViewChild('modal') modal: NgbModal
|
||||
|
||||
error: string
|
||||
|
||||
private openedModal: NgbModalRef
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private modalService: NgbModal,
|
||||
private instanceValidatorsService: InstanceValidatorsService,
|
||||
private instanceService: InstanceService,
|
||||
private serverService: ServerService,
|
||||
private notifier: Notifier,
|
||||
private i18n: I18n
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
get instanceName () {
|
||||
return this.serverService.getConfig().instance.name
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.buildForm({
|
||||
fromName: this.instanceValidatorsService.FROM_NAME,
|
||||
fromEmail: this.instanceValidatorsService.FROM_EMAIL,
|
||||
body: this.instanceValidatorsService.BODY
|
||||
})
|
||||
}
|
||||
|
||||
show () {
|
||||
this.openedModal = this.modalService.open(this.modal, { keyboard: false })
|
||||
}
|
||||
|
||||
hide () {
|
||||
this.form.reset()
|
||||
this.error = undefined
|
||||
|
||||
this.openedModal.close()
|
||||
this.openedModal = null
|
||||
}
|
||||
|
||||
sendForm () {
|
||||
const fromName = this.form.value['fromName']
|
||||
const fromEmail = this.form.value[ 'fromEmail' ]
|
||||
const body = this.form.value[ 'body' ]
|
||||
|
||||
this.instanceService.contactAdministrator(fromEmail, fromName, body)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Your message has been sent.'))
|
||||
this.hide()
|
||||
},
|
||||
|
||||
err => {
|
||||
this.error = err.status === 403
|
||||
? this.i18n('You already sent this form recently')
|
||||
: err.message
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import { AboutComponent } from './about.component'
|
|||
import { SharedModule } from '../shared'
|
||||
import { AboutInstanceComponent } from '@app/+about/about-instance/about-instance.component'
|
||||
import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertube.component'
|
||||
import { ContactAdminModalComponent } from '@app/+about/about-instance/contact-admin-modal.component'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -15,7 +16,8 @@ import { AboutPeertubeComponent } from '@app/+about/about-peertube/about-peertub
|
|||
declarations: [
|
||||
AboutComponent,
|
||||
AboutInstanceComponent,
|
||||
AboutPeertubeComponent
|
||||
AboutPeertubeComponent,
|
||||
ContactAdminModalComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Component, OnInit, OnDestroy } from '@angular/core'
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Account } from '@app/shared/account/account.model'
|
||||
import { AccountService } from '@app/shared/account/account.service'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { MarkdownService } from '@app/videos/shared'
|
||||
import { MarkdownService } from '@app/shared/renderer'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-about',
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
|
|||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { Location } from '@angular/common'
|
||||
import { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
||||
|
@ -13,6 +12,7 @@ import { tap } from 'rxjs/operators'
|
|||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||
import { Notifier } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-videos',
|
||||
|
@ -35,7 +35,7 @@ export class AccountVideosComponent extends AbstractVideoList implements OnInit,
|
|||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected authService: AuthService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected notifier: Notifier,
|
||||
protected confirmService: ConfirmService,
|
||||
protected location: Location,
|
||||
protected screenService: ScreenService,
|
||||
|
|
|
@ -5,10 +5,9 @@ import { Account } from '@app/shared/account/account.model'
|
|||
import { RestExtractor, UserService } from '@app/shared'
|
||||
import { catchError, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService, Notifier, RedirectService } from '@app/core'
|
||||
import { User, UserRight } from '../../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { AuthService, RedirectService } from '@app/core'
|
||||
|
||||
@Component({
|
||||
templateUrl: './accounts.component.html',
|
||||
|
@ -24,11 +23,10 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
private route: ActivatedRoute,
|
||||
private userService: UserService,
|
||||
private accountService: AccountService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private restExtractor: RestExtractor,
|
||||
private redirectService: RedirectService,
|
||||
private authService: AuthService,
|
||||
private i18n: I18n
|
||||
private authService: AuthService
|
||||
) {}
|
||||
|
||||
ngOnInit () {
|
||||
|
@ -43,7 +41,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
.subscribe(
|
||||
account => this.account = account,
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -69,7 +67,7 @@ export class AccountsComponent implements OnInit, OnDestroy {
|
|||
.subscribe(
|
||||
user => this.user = user,
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,63 +7,54 @@
|
|||
|
||||
<div i18n class="inner-form-title">Instance</div>
|
||||
|
||||
<ng-container formGroupName="instance">
|
||||
<div class="form-group">
|
||||
<label i18n for="instanceName">Name</label>
|
||||
<input
|
||||
type="text" id="instanceName"
|
||||
formControlName="instanceName" [ngClass]="{ 'input-error': formErrors['instanceName'] }"
|
||||
formControlName="name" [ngClass]="{ 'input-error': formErrors.instance.name }"
|
||||
>
|
||||
<div *ngIf="formErrors.instanceName" class="form-error">
|
||||
{{ formErrors.instanceName }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.name" class="form-error">{{ formErrors.instance.name }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="instanceShortDescription">Short description</label>
|
||||
<textarea
|
||||
id="instanceShortDescription" formControlName="instanceShortDescription"
|
||||
[ngClass]="{ 'input-error': formErrors['instanceShortDescription'] }"
|
||||
id="instanceShortDescription" formControlName="shortDescription"
|
||||
[ngClass]="{ 'input-error': formErrors['instance.shortDescription'] }"
|
||||
></textarea>
|
||||
<div *ngIf="formErrors.instanceShortDescription" class="form-error">
|
||||
{{ formErrors.instanceShortDescription }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.shortDescription" class="form-error">{{ formErrors.instance.shortDescription }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="instanceDescription">Description</label><my-help helpType="markdownText"></my-help>
|
||||
<my-markdown-textarea
|
||||
id="instanceDescription" formControlName="instanceDescription" textareaWidth="500px" [previewColumn]="true"
|
||||
[classes]="{ 'input-error': formErrors['instanceDescription'] }"
|
||||
id="instanceDescription" formControlName="description" textareaWidth="500px" [previewColumn]="true"
|
||||
[classes]="{ 'input-error': formErrors['instance.description'] }"
|
||||
></my-markdown-textarea>
|
||||
<div *ngIf="formErrors.instanceDescription" class="form-error">
|
||||
{{ formErrors.instanceDescription }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.description" class="form-error">{{ formErrors.instance.description }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="instanceTerms">Terms</label><my-help helpType="markdownText"></my-help>
|
||||
<my-markdown-textarea
|
||||
id="instanceTerms" formControlName="instanceTerms" textareaWidth="500px" [previewColumn]="true"
|
||||
[ngClass]="{ 'input-error': formErrors['instanceTerms'] }"
|
||||
id="instanceTerms" formControlName="terms" textareaWidth="500px" [previewColumn]="true"
|
||||
[ngClass]="{ 'input-error': formErrors['instance.terms'] }"
|
||||
></my-markdown-textarea>
|
||||
<div *ngIf="formErrors.instanceTerms" class="form-error">
|
||||
{{ formErrors.instanceTerms }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.terms" class="form-error">{{ formErrors.instance.terms }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="instanceDefaultClientRoute">Default client route</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="instanceDefaultClientRoute" formControlName="instanceDefaultClientRoute">
|
||||
<select id="instanceDefaultClientRoute" formControlName="defaultClientRoute">
|
||||
<option i18n value="/videos/overview">Videos Overview</option>
|
||||
<option i18n value="/videos/trending">Videos Trending</option>
|
||||
<option i18n value="/videos/recently-added">Videos Recently Added</option>
|
||||
<option i18n value="/videos/local">Local videos</option>
|
||||
</select>
|
||||
</div>
|
||||
<div *ngIf="formErrors.instanceDefaultClientRoute" class="form-error">
|
||||
{{ formErrors.instanceDefaultClientRoute }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.defaultClientRoute" class="form-error">{{ formErrors.instance.defaultClientRoute }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -74,29 +65,29 @@
|
|||
></my-help>
|
||||
|
||||
<div class="peertube-select-container">
|
||||
<select id="instanceDefaultNSFWPolicy" formControlName="instanceDefaultNSFWPolicy">
|
||||
<select id="instanceDefaultNSFWPolicy" formControlName="defaultNSFWPolicy">
|
||||
<option i18n value="do_not_list">Do not list</option>
|
||||
<option i18n value="blur">Blur thumbnails</option>
|
||||
<option i18n value="display">Display</option>
|
||||
</select>
|
||||
</div>
|
||||
<div *ngIf="formErrors.instanceDefaultNSFWPolicy" class="form-error">
|
||||
{{ formErrors.instanceDefaultNSFWPolicy }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.defaultNSFWPolicy" class="form-error">{{ formErrors.instance.defaultNSFWPolicy }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">Signup</div>
|
||||
|
||||
<ng-container formGroupName="signup">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="signupEnabled" formControlName="signupEnabled"
|
||||
inputName="signupEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Signup enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox *ngIf="isSignupEnabled()"
|
||||
inputName="signupRequiresEmailVerification" formControlName="signupRequiresEmailVerification"
|
||||
inputName="signupRequiresEmailVerification" formControlName="requiresEmailVerification"
|
||||
i18n-labelText labelText="Signup requires email verification"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
@ -105,71 +96,80 @@
|
|||
<label i18n for="signupLimit">Signup limit</label>
|
||||
<input
|
||||
type="text" id="signupLimit"
|
||||
formControlName="signupLimit" [ngClass]="{ 'input-error': formErrors['signupLimit'] }"
|
||||
formControlName="limit" [ngClass]="{ 'input-error': formErrors['signup.limit'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.signupLimit" class="form-error">
|
||||
{{ formErrors.signupLimit }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div i18n class="inner-form-title">Import</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="importVideosHttpEnabled" formControlName="importVideosHttpEnabled"
|
||||
i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="importVideosTorrentEnabled" formControlName="importVideosTorrentEnabled"
|
||||
i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div i18n class="inner-form-title">Administrator</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="adminEmail">Admin email</label>
|
||||
<input
|
||||
type="text" id="adminEmail"
|
||||
formControlName="adminEmail" [ngClass]="{ 'input-error': formErrors['adminEmail'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.adminEmail" class="form-error">
|
||||
{{ formErrors.adminEmail }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.signup.limit" class="form-error">{{ formErrors.signup.limit }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">Users</div>
|
||||
|
||||
<ng-container formGroupName="user">
|
||||
<div class="form-group">
|
||||
<label i18n for="userVideoQuota">User default video quota</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="userVideoQuota" formControlName="userVideoQuota">
|
||||
<select id="userVideoQuota" formControlName="videoQuota">
|
||||
<option *ngFor="let videoQuotaOption of videoQuotaOptions" [value]="videoQuotaOption.value">
|
||||
{{ videoQuotaOption.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div *ngIf="formErrors.userVideoQuota" class="form-error">
|
||||
{{ formErrors.userVideoQuota }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.user.videoQuota" class="form-error">{{ formErrors.user.videoQuota }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="userVideoQuotaDaily">User default daily upload limit</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="userVideoQuotaDaily" formControlName="userVideoQuotaDaily">
|
||||
<select id="userVideoQuotaDaily" formControlName="videoQuotaDaily">
|
||||
<option *ngFor="let videoQuotaDailyOption of videoQuotaDailyOptions" [value]="videoQuotaDailyOption.value">
|
||||
{{ videoQuotaDailyOption.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div *ngIf="formErrors.userVideoQuotaDaily" class="form-error">
|
||||
{{ formErrors.userVideoQuotaDaily }}
|
||||
<div *ngIf="formErrors.user.videoQuotaDaily" class="form-error">{{ formErrors.user.videoQuotaDaily }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">Import</div>
|
||||
|
||||
<ng-container formGroupName="import">
|
||||
<ng-container formGroupName="videos">
|
||||
|
||||
<div class="form-group" formGroupName="http">
|
||||
<my-peertube-checkbox
|
||||
inputName="importVideosHttpEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Video import with HTTP URL (i.e. YouTube) enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group" formGroupName="torrent">
|
||||
<my-peertube-checkbox
|
||||
inputName="importVideosTorrentEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Video import with a torrent file or a magnet URI enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">Administrator</div>
|
||||
|
||||
<div class="form-group" formGroupName="admin">
|
||||
<label i18n for="adminEmail">Admin email</label>
|
||||
<input
|
||||
type="text" id="adminEmail"
|
||||
formControlName="email" [ngClass]="{ 'input-error': formErrors['admin.email'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.admin.email" class="form-error">{{ formErrors.admin.email }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" formGroupName="contactForm">
|
||||
<my-peertube-checkbox
|
||||
inputName="enableContactForm" formControlName="enabled"
|
||||
i18n-labelText labelText="Enable contact form"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
|
||||
|
@ -177,6 +177,9 @@
|
|||
<ng-template ngbTabContent>
|
||||
<div i18n class="inner-form-title">Twitter</div>
|
||||
|
||||
<ng-container formGroupName="services">
|
||||
<ng-container formGroupName="twitter">
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="signupLimit">Your Twitter username</label>
|
||||
<my-help
|
||||
|
@ -185,22 +188,24 @@
|
|||
></my-help>
|
||||
<input
|
||||
type="text" id="servicesTwitterUsername"
|
||||
formControlName="servicesTwitterUsername" [ngClass]="{ 'input-error': formErrors['servicesTwitterUsername'] }"
|
||||
formControlName="username" [ngClass]="{ 'input-error': formErrors['services.twitter.username'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.servicesTwitterUsername" class="form-error">
|
||||
{{ formErrors.servicesTwitterUsername }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.services.twitter.username" class="form-error">{{ formErrors.services.twitter.username }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="servicesTwitterWhitelisted" formControlName="servicesTwitterWhitelisted"
|
||||
inputName="servicesTwitterWhitelisted" formControlName="whitelisted"
|
||||
i18n-labelText labelText="Instance whitelisted by Twitter"
|
||||
i18n-helpHtml helpHtml="If your instance is whitelisted by Twitter, a video player will be embedded in the Twitter feed on PeerTube video share.<br />
|
||||
If the instance is not whitelisted, we use an image link card that will redirect on your PeerTube instance.<br /><br />
|
||||
Check this checkbox, save the configuration and test with a video URL of your instance (https://example.com/videos/watch/blabla) on <a target='_blank' rel='noopener noreferrer' href='https://cards-dev.twitter.com/validator'>https://cards-dev.twitter.com/validator</a> to see if you instance is whitelisted."
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
|
||||
|
@ -209,37 +214,48 @@
|
|||
|
||||
<div i18n class="inner-form-title">Transcoding</div>
|
||||
|
||||
<ng-container formGroupName="transcoding">
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="transcodingEnabled" formControlName="transcodingEnabled"
|
||||
inputName="transcodingEnabled" formControlName="enabled"
|
||||
i18n-labelText labelText="Transcoding enabled"
|
||||
i18n-helpHtml helpHtml="If you disable transcoding, many videos from your users will not work!"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<ng-template [ngIf]="isTranscodingEnabled()">
|
||||
<ng-container *ngIf="isTranscodingEnabled()">
|
||||
|
||||
<div class="form-group">
|
||||
<my-peertube-checkbox
|
||||
inputName="transcodingAllowAdditionalExtensions" formControlName="allowAdditionalExtensions"
|
||||
i18n-labelText labelText="Allow additional extensions"
|
||||
i18n-helpHtml helpHtml="Allow your users to upload .mkv, .mov, .avi, .flv videos"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="transcodingThreads">Transcoding threads</label>
|
||||
<div class="peertube-select-container">
|
||||
<select id="transcodingThreads" formControlName="transcodingThreads">
|
||||
<select id="transcodingThreads" formControlName="threads">
|
||||
<option *ngFor="let transcodingThreadOption of transcodingThreadOptions" [value]="transcodingThreadOption.value">
|
||||
{{ transcodingThreadOption.label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div *ngIf="formErrors.transcodingThreads" class="form-error">
|
||||
{{ formErrors.transcodingThreads }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.transcoding.threads" class="form-error">{{ formErrors.transcoding.threads }}</div>
|
||||
</div>
|
||||
|
||||
<ng-container formGroupName="resolutions">
|
||||
<div class="form-group" *ngFor="let resolution of resolutions">
|
||||
<my-peertube-checkbox
|
||||
[inputName]="getResolutionKey(resolution)" [formControlName]="getResolutionKey(resolution)"
|
||||
[inputName]="getResolutionKey(resolution)" [formControlName]="resolution"
|
||||
i18n-labelText labelText="Resolution {{resolution}} enabled"
|
||||
></my-peertube-checkbox>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">
|
||||
Cache
|
||||
|
@ -250,30 +266,30 @@
|
|||
></my-help>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<ng-container formGroupName="cache">
|
||||
<div class="form-group" formGroupName="previews">
|
||||
<label i18n for="cachePreviewsSize">Previews cache size</label>
|
||||
<input
|
||||
type="text" id="cachePreviewsSize"
|
||||
formControlName="cachePreviewsSize" [ngClass]="{ 'input-error': formErrors['cachePreviewsSize'] }"
|
||||
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.previews.size'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.cachePreviewsSize" class="form-error">
|
||||
{{ formErrors.cachePreviewsSize }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.cache.previews.size" class="form-error">{{ formErrors.cache.previews.size }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label i18n for="cachePreviewsSize">Video captions cache size</label>
|
||||
<div class="form-group" formGroupName="captions">
|
||||
<label i18n for="cacheCaptionsSize">Video captions cache size</label>
|
||||
<input
|
||||
type="text" id="cacheCaptionsSize"
|
||||
formControlName="cacheCaptionsSize" [ngClass]="{ 'input-error': formErrors['cacheCaptionsSize'] }"
|
||||
formControlName="size" [ngClass]="{ 'input-error': formErrors['cache.captions.size'] }"
|
||||
>
|
||||
<div *ngIf="formErrors.cacheCaptionsSize" class="form-error">
|
||||
{{ formErrors.cacheCaptionsSize }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.cache.captions.size" class="form-error">{{ formErrors.cache.captions.size }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<div i18n class="inner-form-title">Customizations</div>
|
||||
|
||||
<ng-container formGroupName="instance">
|
||||
<ng-container formGroupName="customizations">
|
||||
<div class="form-group">
|
||||
<label i18n for="customizationJavascript">JavaScript</label>
|
||||
<my-help
|
||||
|
@ -281,12 +297,10 @@
|
|||
customHtml="Write directly JavaScript code.<br />Example: <pre>console.log('my instance is amazing');</pre>"
|
||||
></my-help>
|
||||
<textarea
|
||||
id="customizationJavascript" formControlName="customizationJavascript"
|
||||
[ngClass]="{ 'input-error': formErrors['customizationJavascript'] }"
|
||||
id="customizationJavascript" formControlName="javascript"
|
||||
[ngClass]="{ 'input-error': formErrors['instance.customizations.javascript'] }"
|
||||
></textarea>
|
||||
<div *ngIf="formErrors.customizationJavascript" class="form-error">
|
||||
{{ formErrors.customizationJavascript }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.customizations.javascript" class="form-error">{{ formErrors.instance.customizations.javascript }}</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -311,13 +325,14 @@
|
|||
"
|
||||
></my-help>
|
||||
<textarea
|
||||
id="customizationCSS" formControlName="customizationCSS"
|
||||
[ngClass]="{ 'input-error': formErrors['customizationCSS'] }"
|
||||
id="customizationCSS" formControlName="css"
|
||||
[ngClass]="{ 'input-error': formErrors['instance.customizations.css'] }"
|
||||
></textarea>
|
||||
<div *ngIf="formErrors.customizationCSS" class="form-error">
|
||||
{{ formErrors.customizationCSS }}
|
||||
</div>
|
||||
<div *ngIf="formErrors.instance.customizations.css" class="form-error">{{ formErrors.instance.customizations.css }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
</ng-template>
|
||||
</ngb-tab>
|
||||
</ngb-tabset>
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core'
|
|||
import { ConfigService } from '@app/+admin/config/shared/config.service'
|
||||
import { ServerService } from '@app/core/server/server.service'
|
||||
import { CustomConfigValidatorsService, FormReactive, UserValidatorsService } from '@app/shared'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { CustomConfig } from '../../../../../../shared/models/server/custom-config.model'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { BuildFormDefaultValues, FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
|
@ -18,14 +18,11 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
resolutions: string[] = []
|
||||
transcodingThreadOptions: { label: string, value: number }[] = []
|
||||
|
||||
private oldCustomJavascript: string
|
||||
private oldCustomCSS: string
|
||||
|
||||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private customConfigValidatorsService: CustomConfigValidatorsService,
|
||||
private userValidatorsService: UserValidatorsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private configService: ConfigService,
|
||||
private serverService: ServerService,
|
||||
private i18n: I18n
|
||||
|
@ -58,40 +55,78 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
}
|
||||
|
||||
getResolutionKey (resolution: string) {
|
||||
return 'transcodingResolution' + resolution
|
||||
return 'transcoding.resolutions.' + resolution
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
const formGroupData: { [key: string]: any } = {
|
||||
instanceName: this.customConfigValidatorsService.INSTANCE_NAME,
|
||||
instanceShortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
|
||||
instanceDescription: null,
|
||||
instanceTerms: null,
|
||||
instanceDefaultClientRoute: null,
|
||||
instanceDefaultNSFWPolicy: null,
|
||||
servicesTwitterUsername: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
|
||||
servicesTwitterWhitelisted: null,
|
||||
cachePreviewsSize: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE,
|
||||
cacheCaptionsSize: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE,
|
||||
signupEnabled: null,
|
||||
signupLimit: this.customConfigValidatorsService.SIGNUP_LIMIT,
|
||||
signupRequiresEmailVerification: null,
|
||||
importVideosHttpEnabled: null,
|
||||
importVideosTorrentEnabled: null,
|
||||
adminEmail: this.customConfigValidatorsService.ADMIN_EMAIL,
|
||||
userVideoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
|
||||
userVideoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY,
|
||||
transcodingThreads: this.customConfigValidatorsService.TRANSCODING_THREADS,
|
||||
transcodingEnabled: null,
|
||||
customizationJavascript: null,
|
||||
customizationCSS: null
|
||||
const formGroupData: { [key in keyof CustomConfig ]: any } = {
|
||||
instance: {
|
||||
name: this.customConfigValidatorsService.INSTANCE_NAME,
|
||||
shortDescription: this.customConfigValidatorsService.INSTANCE_SHORT_DESCRIPTION,
|
||||
description: null,
|
||||
terms: null,
|
||||
defaultClientRoute: null,
|
||||
defaultNSFWPolicy: null,
|
||||
customizations: {
|
||||
javascript: null,
|
||||
css: null
|
||||
}
|
||||
},
|
||||
services: {
|
||||
twitter: {
|
||||
username: this.customConfigValidatorsService.SERVICES_TWITTER_USERNAME,
|
||||
whitelisted: null
|
||||
}
|
||||
},
|
||||
cache: {
|
||||
previews: {
|
||||
size: this.customConfigValidatorsService.CACHE_PREVIEWS_SIZE
|
||||
},
|
||||
captions: {
|
||||
size: this.customConfigValidatorsService.CACHE_CAPTIONS_SIZE
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: null,
|
||||
limit: this.customConfigValidatorsService.SIGNUP_LIMIT,
|
||||
requiresEmailVerification: null
|
||||
},
|
||||
import: {
|
||||
videos: {
|
||||
http: {
|
||||
enabled: null
|
||||
},
|
||||
torrent: {
|
||||
enabled: null
|
||||
}
|
||||
}
|
||||
},
|
||||
admin: {
|
||||
email: this.customConfigValidatorsService.ADMIN_EMAIL
|
||||
},
|
||||
contactForm: {
|
||||
enabled: null
|
||||
},
|
||||
user: {
|
||||
videoQuota: this.userValidatorsService.USER_VIDEO_QUOTA,
|
||||
videoQuotaDaily: this.userValidatorsService.USER_VIDEO_QUOTA_DAILY
|
||||
},
|
||||
transcoding: {
|
||||
enabled: null,
|
||||
threads: this.customConfigValidatorsService.TRANSCODING_THREADS,
|
||||
allowAdditionalExtensions: null,
|
||||
resolutions: {}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultValues: BuildFormDefaultValues = {}
|
||||
const defaultValues = {
|
||||
transcoding: {
|
||||
resolutions: {}
|
||||
}
|
||||
}
|
||||
for (const resolution of this.resolutions) {
|
||||
const key = this.getResolutionKey(resolution)
|
||||
defaultValues[key] = 'false'
|
||||
formGroupData[key] = null
|
||||
defaultValues.transcoding.resolutions[resolution] = 'false'
|
||||
formGroupData.transcoding.resolutions[resolution] = null
|
||||
}
|
||||
|
||||
this.buildForm(formGroupData)
|
||||
|
@ -101,90 +136,25 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
res => {
|
||||
this.customConfig = res
|
||||
|
||||
this.oldCustomCSS = this.customConfig.instance.customizations.css
|
||||
this.oldCustomJavascript = this.customConfig.instance.customizations.javascript
|
||||
|
||||
this.updateForm()
|
||||
// Force form validation
|
||||
this.forceCheck()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
isTranscodingEnabled () {
|
||||
return this.form.value['transcodingEnabled'] === true
|
||||
return this.form.value['transcoding']['enabled'] === true
|
||||
}
|
||||
|
||||
isSignupEnabled () {
|
||||
return this.form.value['signupEnabled'] === true
|
||||
return this.form.value['signup']['enabled'] === true
|
||||
}
|
||||
|
||||
async formValidated () {
|
||||
const data: CustomConfig = {
|
||||
instance: {
|
||||
name: this.form.value['instanceName'],
|
||||
shortDescription: this.form.value['instanceShortDescription'],
|
||||
description: this.form.value['instanceDescription'],
|
||||
terms: this.form.value['instanceTerms'],
|
||||
defaultClientRoute: this.form.value['instanceDefaultClientRoute'],
|
||||
defaultNSFWPolicy: this.form.value['instanceDefaultNSFWPolicy'],
|
||||
customizations: {
|
||||
javascript: this.form.value['customizationJavascript'],
|
||||
css: this.form.value['customizationCSS']
|
||||
}
|
||||
},
|
||||
services: {
|
||||
twitter: {
|
||||
username: this.form.value['servicesTwitterUsername'],
|
||||
whitelisted: this.form.value['servicesTwitterWhitelisted']
|
||||
}
|
||||
},
|
||||
cache: {
|
||||
previews: {
|
||||
size: this.form.value['cachePreviewsSize']
|
||||
},
|
||||
captions: {
|
||||
size: this.form.value['cacheCaptionsSize']
|
||||
}
|
||||
},
|
||||
signup: {
|
||||
enabled: this.form.value['signupEnabled'],
|
||||
limit: this.form.value['signupLimit'],
|
||||
requiresEmailVerification: this.form.value['signupRequiresEmailVerification']
|
||||
},
|
||||
admin: {
|
||||
email: this.form.value['adminEmail']
|
||||
},
|
||||
user: {
|
||||
videoQuota: this.form.value['userVideoQuota'],
|
||||
videoQuotaDaily: this.form.value['userVideoQuotaDaily']
|
||||
},
|
||||
transcoding: {
|
||||
enabled: this.form.value['transcodingEnabled'],
|
||||
threads: this.form.value['transcodingThreads'],
|
||||
resolutions: {
|
||||
'240p': this.form.value[this.getResolutionKey('240p')],
|
||||
'360p': this.form.value[this.getResolutionKey('360p')],
|
||||
'480p': this.form.value[this.getResolutionKey('480p')],
|
||||
'720p': this.form.value[this.getResolutionKey('720p')],
|
||||
'1080p': this.form.value[this.getResolutionKey('1080p')]
|
||||
}
|
||||
},
|
||||
import: {
|
||||
videos: {
|
||||
http: {
|
||||
enabled: this.form.value['importVideosHttpEnabled']
|
||||
},
|
||||
torrent: {
|
||||
enabled: this.form.value['importVideosTorrentEnabled']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.configService.updateCustomConfig(data)
|
||||
this.configService.updateCustomConfig(this.form.value)
|
||||
.subscribe(
|
||||
res => {
|
||||
this.customConfig = res
|
||||
|
@ -194,45 +164,15 @@ export class EditCustomConfigComponent extends FormReactive implements OnInit {
|
|||
|
||||
this.updateForm()
|
||||
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Configuration updated.'))
|
||||
this.notifier.success(this.i18n('Configuration updated.'))
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
private updateForm () {
|
||||
const data: { [key: string]: any } = {
|
||||
instanceName: this.customConfig.instance.name,
|
||||
instanceShortDescription: this.customConfig.instance.shortDescription,
|
||||
instanceDescription: this.customConfig.instance.description,
|
||||
instanceTerms: this.customConfig.instance.terms,
|
||||
instanceDefaultClientRoute: this.customConfig.instance.defaultClientRoute,
|
||||
instanceDefaultNSFWPolicy: this.customConfig.instance.defaultNSFWPolicy,
|
||||
servicesTwitterUsername: this.customConfig.services.twitter.username,
|
||||
servicesTwitterWhitelisted: this.customConfig.services.twitter.whitelisted,
|
||||
cachePreviewsSize: this.customConfig.cache.previews.size,
|
||||
cacheCaptionsSize: this.customConfig.cache.captions.size,
|
||||
signupEnabled: this.customConfig.signup.enabled,
|
||||
signupLimit: this.customConfig.signup.limit,
|
||||
signupRequiresEmailVerification: this.customConfig.signup.requiresEmailVerification,
|
||||
adminEmail: this.customConfig.admin.email,
|
||||
userVideoQuota: this.customConfig.user.videoQuota,
|
||||
userVideoQuotaDaily: this.customConfig.user.videoQuotaDaily,
|
||||
transcodingThreads: this.customConfig.transcoding.threads,
|
||||
transcodingEnabled: this.customConfig.transcoding.enabled,
|
||||
customizationJavascript: this.customConfig.instance.customizations.javascript,
|
||||
customizationCSS: this.customConfig.instance.customizations.css,
|
||||
importVideosHttpEnabled: this.customConfig.import.videos.http.enabled,
|
||||
importVideosTorrentEnabled: this.customConfig.import.videos.torrent.enabled
|
||||
}
|
||||
|
||||
for (const resolution of this.resolutions) {
|
||||
const key = this.getResolutionKey(resolution)
|
||||
data[key] = this.customConfig.transcoding.resolutions[resolution]
|
||||
}
|
||||
|
||||
this.form.patchValue(data)
|
||||
this.form.patchValue(this.customConfig)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { SortMeta } from 'primeng/primeng'
|
||||
import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
|
||||
import { RestPagination, RestTable } from '../../../shared'
|
||||
|
@ -20,7 +20,7 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private followService: FollowService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -32,14 +32,14 @@ export class FollowersListComponent extends RestTable implements OnInit {
|
|||
}
|
||||
|
||||
protected loadData () {
|
||||
this.followService.getFollowers(this.pagination, this.sort)
|
||||
this.followService.getFollowers(this.pagination, this.sort, this.search)
|
||||
.subscribe(
|
||||
resultList => {
|
||||
this.followers = resultList.data
|
||||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { ConfirmService } from '../../../core'
|
||||
import { validateHost } from '../../../shared'
|
||||
import { FollowService } from '../shared'
|
||||
|
@ -18,7 +18,7 @@ export class FollowingAddComponent {
|
|||
|
||||
constructor (
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private confirmService: ConfirmService,
|
||||
private followService: FollowService,
|
||||
private i18n: I18n
|
||||
|
@ -64,12 +64,12 @@ export class FollowingAddComponent {
|
|||
|
||||
this.followService.follow(hosts).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Follow request(s) sent!'))
|
||||
this.notifier.success(this.i18n('Follow request(s) sent!'))
|
||||
|
||||
setTimeout(() => this.router.navigate([ '/admin/follows/following-list' ]), 500)
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { SortMeta } from 'primeng/primeng'
|
||||
import { ActorFollow } from '../../../../../../shared/models/actors/follow.model'
|
||||
import { ConfirmService } from '../../../core/confirm/confirm.service'
|
||||
|
@ -20,7 +20,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private confirmService: ConfirmService,
|
||||
private followService: FollowService,
|
||||
private i18n: I18n
|
||||
|
@ -41,14 +41,11 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
|||
|
||||
this.followService.unfollow(follow).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('You are not following {{host}} anymore.', { host: follow.following.host })
|
||||
)
|
||||
this.notifier.success(this.i18n('You are not following {{host}} anymore.', { host: follow.following.host }))
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -60,7 +57,7 @@ export class FollowingListComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { RedundancyService } from '@app/+admin/follows/shared/redundancy.service'
|
||||
|
||||
|
@ -13,7 +13,7 @@ export class RedundancyCheckboxComponent {
|
|||
@Input() host: string
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private redundancyService: RedundancyService,
|
||||
private i18n: I18n
|
||||
) { }
|
||||
|
@ -24,13 +24,10 @@ export class RedundancyCheckboxComponent {
|
|||
() => {
|
||||
const stateLabel = this.redundancyAllowed ? this.i18n('enabled') : this.i18n('disabled')
|
||||
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Redundancy for {{host}} is {{stateLabel}}', { host: this.host, stateLabel })
|
||||
)
|
||||
this.notifier.success(this.i18n('Redundancy for {{host}} is {{stateLabel}}', { host: this.host, stateLabel }))
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { SortMeta } from 'primeng/primeng'
|
||||
import { Job } from '../../../../../../shared/index'
|
||||
import { JobState } from '../../../../../../shared/models'
|
||||
|
@ -25,7 +25,7 @@ export class JobsListComponent extends RestTable implements OnInit {
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private jobsService: JobService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -53,7 +53,7 @@ export class JobsListComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { BlocklistService, AccountBlock } from '@app/shared/blocklist'
|
||||
import { AccountBlock, BlocklistService } from '@app/shared/blocklist'
|
||||
|
||||
@Component({
|
||||
selector: 'my-instance-account-blocklist',
|
||||
|
@ -18,7 +18,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private blocklistService: BlocklistService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -35,8 +35,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
|
|||
this.blocklistService.unblockAccountByInstance(blockedAccount)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.notifier.success(
|
||||
this.i18n('Account {{nameWithHost}} unmuted by your instance.', { nameWithHost: blockedAccount.nameWithHost })
|
||||
)
|
||||
|
||||
|
@ -53,7 +52,7 @@ export class InstanceAccountBlocklistComponent extends RestTable implements OnIn
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
|
@ -19,7 +19,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private blocklistService: BlocklistService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -36,10 +36,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
|
|||
this.blocklistService.unblockServerByInstance(host)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Instance {{host}} unmuted by your instance.', { host })
|
||||
)
|
||||
this.notifier.success(this.i18n('Instance {{host}} unmuted by your instance.', { host }))
|
||||
|
||||
this.loadData()
|
||||
}
|
||||
|
@ -54,7 +51,7 @@ export class InstanceServerBlocklistComponent extends RestTable implements OnIni
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
font-weight: $font-semibold;
|
||||
min-width: 200px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.moderation-expanded-text {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<ng-template #modal>
|
||||
<div class="modal-header">
|
||||
<h4 i18n class="modal-title">Moderation comment</h4>
|
||||
<span class="close" aria-hidden="true" (click)="hideModerationCommentModal()"></span>
|
||||
|
||||
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="hide()"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
@ -14,12 +15,12 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div i18n>
|
||||
<div class="form-group" i18n>
|
||||
This comment can only be seen by you or the other moderators.
|
||||
</div>
|
||||
|
||||
<div class="form-group inputs">
|
||||
<span i18n class="action-button action-button-cancel" (click)="hideModerationCommentModal()">Cancel</span>
|
||||
<span i18n class="action-button action-button-cancel" (click)="hide()">Cancel</span>
|
||||
|
||||
<input
|
||||
type="submit" i18n-value value="Update this comment" class="action-button-submit"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { FormReactive, VideoAbuseService, VideoAbuseValidatorsService } from '../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
@ -22,7 +22,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
|
|||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private modalService: NgbModal,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private videoAbuseService: VideoAbuseService,
|
||||
private videoAbuseValidatorsService: VideoAbuseValidatorsService,
|
||||
private i18n: I18n
|
||||
|
@ -45,7 +45,7 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
|
|||
})
|
||||
}
|
||||
|
||||
hideModerationCommentModal () {
|
||||
hide () {
|
||||
this.abuseToComment = undefined
|
||||
this.openedModal.close()
|
||||
this.form.reset()
|
||||
|
@ -57,16 +57,13 @@ export class ModerationCommentModalComponent extends FormReactive implements OnI
|
|||
this.videoAbuseService.updateVideoAbuse(this.abuseToComment, { moderationComment })
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Comment updated.')
|
||||
)
|
||||
this.notifier.success(this.i18n('Comment updated.'))
|
||||
|
||||
this.commentUpdated.emit(moderationComment)
|
||||
this.hideModerationCommentModal()
|
||||
this.hide()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</td>
|
||||
|
||||
<td class="action-cell">
|
||||
<my-action-dropdown i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
|
||||
<my-action-dropdown placement="bottom-right" i18n-label label="Actions" [actions]="videoAbuseActions" [entry]="videoAbuse"></my-action-dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
@ -51,11 +51,11 @@
|
|||
<td class="moderation-expanded" colspan="6">
|
||||
<div>
|
||||
<span i18n class="moderation-expanded-label">Reason:</span>
|
||||
<span class="moderation-expanded-text">{{ videoAbuse.reason }}</span>
|
||||
<span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.reason)"></span>
|
||||
</div>
|
||||
<div *ngIf="videoAbuse.moderationComment">
|
||||
<span i18n class="moderation-expanded-label">Moderation comment:</span>
|
||||
<span class="moderation-expanded-text">{{ videoAbuse.moderationComment }}</span>
|
||||
<span class="moderation-expanded-text" [innerHTML]="toHtml(videoAbuse.moderationComment)"></span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { Account } from '../../../shared/account/account.model'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { VideoAbuse, VideoAbuseState } from '../../../../../../shared'
|
||||
import { RestPagination, RestTable, VideoAbuseService } from '../../../shared'
|
||||
|
@ -9,6 +9,7 @@ import { DropdownAction } from '../../../shared/buttons/action-dropdown.componen
|
|||
import { ConfirmService } from '../../../core/index'
|
||||
import { ModerationCommentModalComponent } from './moderation-comment-modal.component'
|
||||
import { Video } from '../../../shared/video/video.model'
|
||||
import { MarkdownService } from '@app/shared/renderer'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-abuse-list',
|
||||
|
@ -27,10 +28,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
|||
videoAbuseActions: DropdownAction<VideoAbuse>[] = []
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private videoAbuseService: VideoAbuseService,
|
||||
private confirmService: ConfirmService,
|
||||
private i18n: I18n
|
||||
private i18n: I18n,
|
||||
private markdownRenderer: MarkdownService
|
||||
) {
|
||||
super()
|
||||
|
||||
|
@ -90,14 +92,11 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
|||
|
||||
this.videoAbuseService.removeVideoAbuse(videoAbuse).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Abuse deleted.')
|
||||
)
|
||||
this.notifier.success(this.i18n('Abuse deleted.'))
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -106,11 +105,15 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
|||
.subscribe(
|
||||
() => this.loadData(),
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
toHtml (text: string) {
|
||||
return this.markdownRenderer.textMarkdownToHTML(text)
|
||||
}
|
||||
|
||||
protected loadData () {
|
||||
return this.videoAbuseService.getVideoAbuses(this.pagination, this.sort)
|
||||
.subscribe(
|
||||
|
@ -119,7 +122,7 @@ export class VideoAbuseListComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
<th style="width: 40px"></th>
|
||||
<th i18n pSortableColumn="name">Video name <p-sortIcon field="name"></p-sortIcon></th>
|
||||
<th i18n>Sensitive</th>
|
||||
<th i18n>Unfederated</th>
|
||||
<th i18n pSortableColumn="createdAt">Date <p-sortIcon field="createdAt"></p-sortIcon></th>
|
||||
<th style="width: 120px;"></th>
|
||||
</tr>
|
||||
|
@ -26,20 +27,21 @@
|
|||
</a>
|
||||
</td>
|
||||
|
||||
<td>{{ videoBlacklist.video.nsfw }}</td>
|
||||
<td>{{ booleanToText(videoBlacklist.video.nsfw) }}</td>
|
||||
<td>{{ booleanToText(videoBlacklist.unfederated) }}</td>
|
||||
<td>{{ videoBlacklist.createdAt }}</td>
|
||||
|
||||
<td class="action-cell">
|
||||
<my-action-dropdown i18n-label label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"></my-action-dropdown>
|
||||
<my-action-dropdown i18n-label placement="bottom-right" label="Actions" [actions]="videoBlacklistActions" [entry]="videoBlacklist"></my-action-dropdown>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
||||
<ng-template pTemplate="rowexpansion" let-videoBlacklist>
|
||||
<tr>
|
||||
<td class="moderation-expanded" colspan="5">
|
||||
<td class="moderation-expanded" colspan="6">
|
||||
<span i18n class="moderation-expanded-label">Blacklist reason:</span>
|
||||
<span class="moderation-expanded-text">{{ videoBlacklist.reason }}</span>
|
||||
<span class="moderation-expanded-text" [innerHTML]="toHtml(videoBlacklist.reason)"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { ConfirmService } from '../../../core'
|
||||
import { RestPagination, RestTable, VideoBlacklistService } from '../../../shared'
|
||||
import { VideoBlacklist } from '../../../../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { DropdownAction } from '../../../shared/buttons/action-dropdown.component'
|
||||
import { Video } from '../../../shared/video/video.model'
|
||||
import { MarkdownService } from '@app/shared/renderer'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-blacklist-list',
|
||||
|
@ -23,9 +24,10 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
|||
videoBlacklistActions: DropdownAction<VideoBlacklist>[] = []
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private confirmService: ConfirmService,
|
||||
private videoBlacklistService: VideoBlacklistService,
|
||||
private markdownRenderer: MarkdownService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
super()
|
||||
|
@ -46,6 +48,16 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
|||
return Video.buildClientUrl(videoBlacklist.video.uuid)
|
||||
}
|
||||
|
||||
booleanToText (value: boolean) {
|
||||
if (value === true) return this.i18n('yes')
|
||||
|
||||
return this.i18n('no')
|
||||
}
|
||||
|
||||
toHtml (text: string) {
|
||||
return this.markdownRenderer.textMarkdownToHTML(text)
|
||||
}
|
||||
|
||||
async removeVideoFromBlacklist (entry: VideoBlacklist) {
|
||||
const confirmMessage = this.i18n(
|
||||
'Do you really want to remove this video from the blacklist? It will be available again in the videos list.'
|
||||
|
@ -56,14 +68,11 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
|||
|
||||
this.videoBlacklistService.removeVideoFromBlacklist(entry.video.id).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name })
|
||||
)
|
||||
this.notifier.success(this.i18n('Video {{name}} removed from the blacklist.', { name: entry.video.name }))
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -75,7 +84,7 @@ export class VideoBlacklistListComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { ServerService } from '../../../core'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { UserCreate, UserRole } from '../../../../../../shared'
|
||||
import { UserEdit } from './user-edit'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
|
@ -24,7 +23,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
|
|||
protected configService: ConfigService,
|
||||
private userValidatorsService: UserValidatorsService,
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -60,10 +59,7 @@ export class UserCreateComponent extends UserEdit implements OnInit {
|
|||
|
||||
this.userService.addUser(userCreate).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('User {{username}} created.', { username: userCreate.username })
|
||||
)
|
||||
this.notifier.success(this.i18n('User {{username}} created.', { username: userCreate.username }))
|
||||
this.router.navigate([ '/admin/users/list' ])
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { ServerService } from '../../../core'
|
||||
import { UserEdit } from './user-edit'
|
||||
import { User, UserUpdate } from '../../../../../../shared'
|
||||
|
@ -30,7 +30,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
|
|||
private userValidatorsService: UserValidatorsService,
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -73,10 +73,7 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy {
|
|||
|
||||
this.userService.updateUser(this.userId, userUpdate).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('User {{username}} updated.', { username: this.username })
|
||||
)
|
||||
this.notifier.success(this.i18n('User {{username}} updated.', { username: this.username }))
|
||||
this.router.navigate([ '/admin/users/list' ])
|
||||
},
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div i18n class="form-sub-title">Users list</div>
|
||||
|
||||
<a class="add-button" routerLink="/admin/users/create">
|
||||
<span class="icon icon-add"></span>
|
||||
<my-global-icon iconName="add"></my-global-icon>
|
||||
<ng-container i18n>Create user</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -65,7 +65,9 @@
|
|||
<span i18n *ngIf="user.blocked" class="banned-info">(banned)</span>
|
||||
</a>
|
||||
</td>
|
||||
|
||||
<td *ngIf="!requiresEmailVerification || user.blocked; else emailWithVerificationStatus">{{ user.email }}</td>
|
||||
|
||||
<ng-template #emailWithVerificationStatus>
|
||||
<td *ngIf="user.emailVerified === false; else emailVerifiedNotFalse" i18n-title title="User's email must be verified to login">
|
||||
<em>? {{ user.email }}</em>
|
||||
|
@ -76,6 +78,7 @@
|
|||
</td>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<td>{{ user.videoQuotaUsed }} / {{ user.videoQuota }}</td>
|
||||
<td>{{ user.roleLabel }}</td>
|
||||
<td>{{ user.createdAt }}</td>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import '_mixins';
|
||||
|
||||
.add-button {
|
||||
@include create-button('../../../../assets/images/global/add.svg');
|
||||
@include create-button;
|
||||
}
|
||||
|
||||
tr.banned {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { ConfirmService, ServerService } from '../../../core'
|
||||
import { RestPagination, RestTable, UserService } from '../../../shared'
|
||||
|
@ -26,7 +26,7 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
bulkUserActions: DropdownAction<User[]>[] = []
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private confirmService: ConfirmService,
|
||||
private serverService: ServerService,
|
||||
private userService: UserService,
|
||||
|
@ -68,7 +68,7 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
openBanUserModal (users: User[]) {
|
||||
for (const user of users) {
|
||||
if (user.username === 'root') {
|
||||
this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot ban root.'))
|
||||
this.notifier.error(this.i18n('You cannot ban root.'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -91,18 +91,18 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
() => {
|
||||
const message = this.i18n('{{num}} users unbanned.', { num: users.length })
|
||||
|
||||
this.notificationsService.success(this.i18n('Success'), message)
|
||||
this.notifier.success(message)
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
async removeUsers (users: User[]) {
|
||||
for (const user of users) {
|
||||
if (user.username === 'root') {
|
||||
this.notificationsService.error(this.i18n('Error'), this.i18n('You cannot delete root.'))
|
||||
this.notifier.error(this.i18n('You cannot delete root.'))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -113,28 +113,22 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
|
||||
this.userService.removeUser(users).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('{{num}} users deleted.', { num: users.length })
|
||||
)
|
||||
this.notifier.success(this.i18n('{{num}} users deleted.', { num: users.length }))
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
async setEmailsAsVerified (users: User[]) {
|
||||
this.userService.updateUsers(users, { emailVerified: true }).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('{{num}} users email set as verified.', { num: users.length })
|
||||
)
|
||||
this.notifier.success(this.i18n('{{num}} users email set as verified.', { num: users.length }))
|
||||
this.loadData()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -152,7 +146,7 @@ export class UserListComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { BlocklistService, AccountBlock } from '@app/shared/blocklist'
|
||||
import { AccountBlock, BlocklistService } from '@app/shared/blocklist'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-blocklist',
|
||||
|
@ -18,7 +18,7 @@ export class MyAccountBlocklistComponent extends RestTable implements OnInit {
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private blocklistService: BlocklistService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -35,10 +35,7 @@ export class MyAccountBlocklistComponent extends RestTable implements OnInit {
|
|||
this.blocklistService.unblockAccountByUser(blockedAccount)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: blockedAccount.nameWithHost })
|
||||
)
|
||||
this.notifier.success(this.i18n('Account {{nameWithHost}} unmuted.', { nameWithHost: blockedAccount.nameWithHost }))
|
||||
|
||||
this.loadData()
|
||||
}
|
||||
|
@ -53,7 +50,7 @@ export class MyAccountBlocklistComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
|
@ -19,7 +19,7 @@ export class MyAccountServerBlocklistComponent extends RestTable implements OnIn
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private blocklistService: BlocklistService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -36,10 +36,7 @@ export class MyAccountServerBlocklistComponent extends RestTable implements OnIn
|
|||
this.blocklistService.unblockServerByUser(host)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Instance {{host}} unmuted.', { host })
|
||||
)
|
||||
this.notifier.success(this.i18n('Instance {{host}} unmuted.', { host }))
|
||||
|
||||
this.loadData()
|
||||
}
|
||||
|
@ -54,7 +51,7 @@ export class MyAccountServerBlocklistComponent extends RestTable implements OnIn
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<div class="top-buttons">
|
||||
<div class="history-switch">
|
||||
<p-inputSwitch [(ngModel)]="videosHistoryEnabled" (ngModelChange)="onVideosHistoryChange()"></p-inputSwitch>
|
||||
<label i18n>History enabled</label>
|
||||
</div>
|
||||
|
||||
<div class="delete-history">
|
||||
<button (click)="deleteHistory()" i18n>Delete history</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="no-history" i18n *ngIf="pagination.totalItems === 0">You don't have videos history yet.</div>
|
||||
|
||||
<div myInfiniteScroller (nearOfBottom)="onNearOfBottom()" class="videos" #videosElement>
|
||||
<div *ngFor="let videos of videoPages;" class="videos-page">
|
||||
<div class="video" *ngFor="let video of videos">
|
||||
<my-video-thumbnail [video]="video"></my-video-thumbnail>
|
||||
|
||||
<div class="video-info">
|
||||
<a tabindex="-1" class="video-info-name" [routerLink]="['/videos/watch', video.uuid]" [attr.title]="video.name">{{ video.name }}</a>
|
||||
<span i18n class="video-info-date-views">{{ video.views | myNumberFormatter }} views</span>
|
||||
<a tabindex="-1" class="video-info-account" [routerLink]="[ '/accounts', video.byAccount ]">{{ video.byAccount }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,99 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.no-history {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 50px;
|
||||
font-weight: $font-semibold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.top-buttons {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
|
||||
.history-switch {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
|
||||
label {
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.delete-history {
|
||||
font-size: 15px;
|
||||
|
||||
button {
|
||||
@include peertube-button;
|
||||
@include grey-button;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.video {
|
||||
@include row-blocks;
|
||||
|
||||
my-video-thumbnail {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.video-info {
|
||||
flex-grow: 1;
|
||||
|
||||
.video-info-name {
|
||||
@include disable-default-a-behaviour;
|
||||
|
||||
color: var(--mainForegroundColor);
|
||||
display: block;
|
||||
width: fit-content;
|
||||
font-size: 18px;
|
||||
font-weight: $font-semibold;
|
||||
}
|
||||
|
||||
.video-info-date-views {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.video-info-account {
|
||||
@include disable-default-a-behaviour;
|
||||
|
||||
display: block;
|
||||
width: fit-content;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 14px;
|
||||
color: $grey-foreground-color;
|
||||
|
||||
&:hover {
|
||||
color: $grey-foreground-hover-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $small-view) {
|
||||
.video {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
|
||||
.video-info-name {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
my-video-thumbnail {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.video-buttons {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { Location } from '@angular/common'
|
||||
import { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
||||
import { VideoService } from '../../shared/video/video.service'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||
import { UserHistoryService } from '@app/shared/users/user-history.service'
|
||||
import { UserService } from '@app/shared'
|
||||
import { Notifier } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-history',
|
||||
templateUrl: './my-account-history.component.html',
|
||||
styleUrls: [ './my-account-history.component.scss' ]
|
||||
})
|
||||
export class MyAccountHistoryComponent extends AbstractVideoList implements OnInit, OnDestroy {
|
||||
titlePage: string
|
||||
currentRoute = '/my-account/history/videos'
|
||||
pagination: ComponentPagination = {
|
||||
currentPage: 1,
|
||||
itemsPerPage: 5,
|
||||
totalItems: null
|
||||
}
|
||||
videosHistoryEnabled: boolean
|
||||
|
||||
protected baseVideoWidth = -1
|
||||
protected baseVideoHeight = 155
|
||||
|
||||
constructor (
|
||||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected authService: AuthService,
|
||||
protected userService: UserService,
|
||||
protected notifier: Notifier,
|
||||
protected location: Location,
|
||||
protected screenService: ScreenService,
|
||||
protected i18n: I18n,
|
||||
private confirmService: ConfirmService,
|
||||
private videoService: VideoService,
|
||||
private userHistoryService: UserHistoryService
|
||||
) {
|
||||
super()
|
||||
|
||||
this.titlePage = this.i18n('My videos history')
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
super.ngOnInit()
|
||||
|
||||
this.videosHistoryEnabled = this.authService.getUser().videosHistoryEnabled
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
super.ngOnDestroy()
|
||||
}
|
||||
|
||||
getVideosObservable (page: number) {
|
||||
const newPagination = immutableAssign(this.pagination, { currentPage: page })
|
||||
|
||||
return this.userHistoryService.getUserVideosHistory(newPagination)
|
||||
}
|
||||
|
||||
generateSyndicationList () {
|
||||
throw new Error('Method not implemented.')
|
||||
}
|
||||
|
||||
onVideosHistoryChange () {
|
||||
this.userService.updateMyProfile({ videosHistoryEnabled: this.videosHistoryEnabled })
|
||||
.subscribe(
|
||||
() => {
|
||||
const message = this.videosHistoryEnabled === true ?
|
||||
this.i18n('Videos history is enabled') :
|
||||
this.i18n('Videos history is disabled')
|
||||
|
||||
this.notifier.success(message)
|
||||
|
||||
this.authService.refreshUserInformation()
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
async deleteHistory () {
|
||||
const title = this.i18n('Delete videos history')
|
||||
const message = this.i18n('Are you sure you want to delete all your videos history?')
|
||||
|
||||
const res = await this.confirmService.confirm(message, title)
|
||||
if (res !== true) return
|
||||
|
||||
this.userHistoryService.deleteUserVideosHistory()
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Videos history deleted'))
|
||||
|
||||
this.reloadVideos()
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<div class="header">
|
||||
<a routerLink="/my-account/settings" fragment="notifications" i18n>
|
||||
<my-global-icon iconName="cog"></my-global-icon>
|
||||
Notification preferences
|
||||
</a>
|
||||
|
||||
<button (click)="markAllAsRead()" i18n>
|
||||
<my-global-icon iconName="circle-tick"></my-global-icon>
|
||||
Mark all as read
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<my-user-notifications #userNotification></my-user-notifications>
|
|
@ -0,0 +1,25 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 15px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
a {
|
||||
@include peertube-button-link;
|
||||
@include grey-button;
|
||||
@include button-with-icon(18px, 3px, -1px);
|
||||
}
|
||||
|
||||
button {
|
||||
@include peertube-button;
|
||||
@include grey-button;
|
||||
@include button-with-icon(20px, 3px, -1px);
|
||||
}
|
||||
}
|
||||
|
||||
my-user-notifications {
|
||||
font-size: 15px;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { Component, ViewChild } from '@angular/core'
|
||||
import { UserNotificationsComponent } from '@app/shared'
|
||||
|
||||
@Component({
|
||||
templateUrl: './my-account-notifications.component.html',
|
||||
styleUrls: [ './my-account-notifications.component.scss' ]
|
||||
})
|
||||
export class MyAccountNotificationsComponent {
|
||||
@ViewChild('userNotification') userNotification: UserNotificationsComponent
|
||||
|
||||
markAllAsRead () {
|
||||
this.userNotification.markAllAsRead()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
<ng-template #modal let-close="close" let-dismiss="dismiss">
|
||||
<div class="modal-header">
|
||||
<h4 i18n class="modal-title">Accept ownership</h4>
|
||||
<span class="close" aria-label="Close" role="button" (click)="dismiss()"></span>
|
||||
|
||||
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div class="modal-body" [formGroup]="form">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, ElementRef, EventEmitter, OnInit, Output, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService, Notifier } from '@app/core'
|
||||
import { FormReactive } from '@app/shared'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { VideoOwnershipService } from '@app/shared/video-ownership'
|
||||
|
@ -8,7 +8,6 @@ import { VideoAcceptOwnershipValidatorsService } from '@app/shared/forms/form-va
|
|||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { AuthService } from '@app/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
|
||||
@Component({
|
||||
|
@ -31,7 +30,7 @@ export class MyAccountAcceptOwnershipComponent extends FormReactive implements O
|
|||
protected formValidatorService: FormValidatorService,
|
||||
private videoChangeOwnershipValidatorsService: VideoAcceptOwnershipValidatorsService,
|
||||
private videoOwnershipService: VideoOwnershipService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private authService: AuthService,
|
||||
private videoChannelService: VideoChannelService,
|
||||
private modalService: NgbModal,
|
||||
|
@ -68,12 +67,12 @@ export class MyAccountAcceptOwnershipComponent extends FormReactive implements O
|
|||
.acceptOwnership(videoChangeOwnership.id, { channelId: channel })
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Ownership accepted'))
|
||||
this.notifier.success(this.i18n('Ownership accepted'))
|
||||
if (this.accepted) this.accepted.emit()
|
||||
this.videoChangeOwnership = undefined
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,10 +40,10 @@
|
|||
<td class="action-cell">
|
||||
<ng-container *ngIf="videoChangeOwnership.status === 'WAITING'">
|
||||
<my-button i18n label="Accept"
|
||||
icon="icon-tick"
|
||||
icon="tick"
|
||||
(click)="openAcceptModal(videoChangeOwnership)"></my-button>
|
||||
<my-button i18n label="Refuse"
|
||||
icon="icon-cross"
|
||||
icon="cross"
|
||||
(click)="refuse(videoChangeOwnership)">Refuse</my-button>
|
||||
</ng-container>
|
||||
</td>
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Notifier } from '@app/core'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { VideoChangeOwnership } from '../../../../../shared'
|
||||
import { VideoOwnershipService } from '@app/shared/video-ownership'
|
||||
import { Account } from '@app/shared/account/account.model'
|
||||
import { MyAccountAcceptOwnershipComponent }
|
||||
from '@app/+my-account/my-account-ownership/my-account-accept-ownership/my-account-accept-ownership.component'
|
||||
import { MyAccountAcceptOwnershipComponent } from './my-account-accept-ownership/my-account-accept-ownership.component'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-ownership',
|
||||
|
@ -23,9 +21,8 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
|
|||
@ViewChild('myAccountAcceptOwnershipComponent') myAccountAcceptOwnershipComponent: MyAccountAcceptOwnershipComponent
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private videoOwnershipService: VideoOwnershipService,
|
||||
private i18n: I18n
|
||||
private notifier: Notifier,
|
||||
private videoOwnershipService: VideoOwnershipService
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
@ -50,7 +47,7 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
|
|||
this.videoOwnershipService.refuseOwnership(videoChangeOwnership.id)
|
||||
.subscribe(
|
||||
() => this.loadData(),
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -62,7 +59,7 @@ export class MyAccountOwnershipComponent extends RestTable implements OnInit {
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-sub
|
|||
import { MyAccountOwnershipComponent } from '@app/+my-account/my-account-ownership/my-account-ownership.component'
|
||||
import { MyAccountBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-blocklist.component'
|
||||
import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component'
|
||||
import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component'
|
||||
import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component'
|
||||
|
||||
const myAccountRoutes: Routes = [
|
||||
{
|
||||
|
@ -114,6 +116,24 @@ const myAccountRoutes: Routes = [
|
|||
title: 'Muted instances'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'history/videos',
|
||||
component: MyAccountHistoryComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Videos history'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'notifications',
|
||||
component: MyAccountNotificationsComponent,
|
||||
data: {
|
||||
meta: {
|
||||
title: 'Notifications'
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService, Notifier } from '@app/core'
|
||||
import { FormReactive, UserService } from '../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { AuthService } from '@app/core'
|
||||
import { User } from '../../../../../../shared'
|
||||
|
||||
@Component({
|
||||
|
@ -20,7 +19,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
|
|||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private userValidatorsService: UserValidatorsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private authService: AuthService,
|
||||
private userService: UserService,
|
||||
private i18n: I18n
|
||||
|
@ -50,7 +49,7 @@ export class MyAccountChangePasswordComponent extends FormReactive implements On
|
|||
|
||||
this.userService.changePassword(currentPassword, newPassword).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Password updated.'))
|
||||
this.notifier.success(this.i18n('Password updated.'))
|
||||
|
||||
this.form.reset()
|
||||
this.error = null
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Input } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { AuthService, ConfirmService, RedirectService } from '../../../core'
|
||||
import { UserService } from '../../../shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
|
@ -15,7 +15,7 @@ export class MyAccountDangerZoneComponent {
|
|||
|
||||
constructor (
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private confirmService: ConfirmService,
|
||||
private redirectService: RedirectService,
|
||||
|
@ -34,13 +34,13 @@ export class MyAccountDangerZoneComponent {
|
|||
|
||||
this.userService.deleteMe().subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Your account is deleted.'))
|
||||
this.notifier.success(this.i18n('Your account is deleted.'))
|
||||
|
||||
this.authService.logout()
|
||||
this.redirectService.redirectToHomepage()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
export * from './my-account-notification-preferences.component'
|
|
@ -0,0 +1,19 @@
|
|||
<div class="custom-row">
|
||||
<div i18n>Activities</div>
|
||||
<div i18n>Web</div>
|
||||
<div i18n *ngIf="emailEnabled">Email</div>
|
||||
</div>
|
||||
|
||||
<div class="custom-row" *ngFor="let notificationType of notificationSettingKeys">
|
||||
<ng-container *ngIf="hasUserRight(notificationType)">
|
||||
<div>{{ labelNotifications[notificationType] }}</div>
|
||||
|
||||
<div>
|
||||
<p-inputSwitch [(ngModel)]="webNotifications[notificationType]" (onChange)="updateWebSetting(notificationType, $event.checked)"></p-inputSwitch>
|
||||
</div>
|
||||
|
||||
<div *ngIf="emailEnabled">
|
||||
<p-inputSwitch [(ngModel)]="emailNotifications[notificationType]" (onChange)="updateEmailSetting(notificationType, $event.checked)"></p-inputSwitch>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
|
@ -0,0 +1,25 @@
|
|||
@import '_variables';
|
||||
@import '_mixins';
|
||||
|
||||
.custom-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.10);
|
||||
|
||||
&:first-child {
|
||||
font-size: 16px;
|
||||
|
||||
& > div {
|
||||
font-weight: $font-semibold;
|
||||
}
|
||||
}
|
||||
|
||||
& > div {
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
padding: 10px
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
import { Component, Input, OnInit } from '@angular/core'
|
||||
import { User } from '@app/shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subject } from 'rxjs'
|
||||
import { UserNotificationSetting, UserNotificationSettingValue, UserRight } from '../../../../../../shared'
|
||||
import { Notifier, ServerService } from '@app/core'
|
||||
import { debounce } from 'lodash-es'
|
||||
import { UserNotificationService } from '@app/shared/users/user-notification.service'
|
||||
|
||||
@Component({
|
||||
selector: 'my-account-notification-preferences',
|
||||
templateUrl: './my-account-notification-preferences.component.html',
|
||||
styleUrls: [ './my-account-notification-preferences.component.scss' ]
|
||||
})
|
||||
export class MyAccountNotificationPreferencesComponent implements OnInit {
|
||||
@Input() user: User = null
|
||||
@Input() userInformationLoaded: Subject<any>
|
||||
|
||||
notificationSettingKeys: (keyof UserNotificationSetting)[] = []
|
||||
emailNotifications: { [ id in keyof UserNotificationSetting ]: boolean } = {} as any
|
||||
webNotifications: { [ id in keyof UserNotificationSetting ]: boolean } = {} as any
|
||||
labelNotifications: { [ id in keyof UserNotificationSetting ]: string } = {} as any
|
||||
rightNotifications: { [ id in keyof Partial<UserNotificationSetting> ]: UserRight } = {} as any
|
||||
emailEnabled: boolean
|
||||
|
||||
private savePreferences = debounce(this.savePreferencesImpl.bind(this), 500)
|
||||
|
||||
constructor (
|
||||
private i18n: I18n,
|
||||
private userNotificationService: UserNotificationService,
|
||||
private serverService: ServerService,
|
||||
private notifier: Notifier
|
||||
) {
|
||||
this.labelNotifications = {
|
||||
newVideoFromSubscription: this.i18n('New video from your subscriptions'),
|
||||
newCommentOnMyVideo: this.i18n('New comment on your video'),
|
||||
videoAbuseAsModerator: this.i18n('New video abuse on local video'),
|
||||
blacklistOnMyVideo: this.i18n('One of your video is blacklisted/unblacklisted'),
|
||||
myVideoPublished: this.i18n('Video published (after transcoding/scheduled update)'),
|
||||
myVideoImportFinished: this.i18n('Video import finished'),
|
||||
newUserRegistration: this.i18n('A new user registered on your instance'),
|
||||
newFollow: this.i18n('You or your channel(s) has a new follower'),
|
||||
commentMention: this.i18n('Someone mentioned you in video comments')
|
||||
}
|
||||
this.notificationSettingKeys = Object.keys(this.labelNotifications) as (keyof UserNotificationSetting)[]
|
||||
|
||||
this.rightNotifications = {
|
||||
videoAbuseAsModerator: UserRight.MANAGE_VIDEO_ABUSES,
|
||||
newUserRegistration: UserRight.MANAGE_USERS
|
||||
}
|
||||
|
||||
this.emailEnabled = this.serverService.getConfig().email.enabled
|
||||
}
|
||||
|
||||
ngOnInit () {
|
||||
this.userInformationLoaded.subscribe(() => this.loadNotificationSettings())
|
||||
}
|
||||
|
||||
hasUserRight (field: keyof UserNotificationSetting) {
|
||||
const rightToHave = this.rightNotifications[field]
|
||||
if (!rightToHave) return true // No rights needed
|
||||
|
||||
return this.user.hasRight(rightToHave)
|
||||
}
|
||||
|
||||
updateEmailSetting (field: keyof UserNotificationSetting, value: boolean) {
|
||||
if (value === true) this.user.notificationSettings[field] |= UserNotificationSettingValue.EMAIL
|
||||
else this.user.notificationSettings[field] &= ~UserNotificationSettingValue.EMAIL
|
||||
|
||||
this.savePreferences()
|
||||
}
|
||||
|
||||
updateWebSetting (field: keyof UserNotificationSetting, value: boolean) {
|
||||
if (value === true) this.user.notificationSettings[field] |= UserNotificationSettingValue.WEB
|
||||
else this.user.notificationSettings[field] &= ~UserNotificationSettingValue.WEB
|
||||
|
||||
this.savePreferences()
|
||||
}
|
||||
|
||||
private savePreferencesImpl () {
|
||||
this.userNotificationService.updateNotificationSettings(this.user, this.user.notificationSettings)
|
||||
.subscribe(
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Preferences saved'), undefined, 2000)
|
||||
},
|
||||
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
private loadNotificationSettings () {
|
||||
for (const key of Object.keys(this.user.notificationSettings)) {
|
||||
const value = this.user.notificationSettings[key]
|
||||
this.emailNotifications[key] = value & UserNotificationSettingValue.EMAIL
|
||||
|
||||
this.webNotifications[key] = value & UserNotificationSettingValue.WEB
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Input, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { FormReactive, UserService } from '../../../shared'
|
||||
import { User } from '@app/shared'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
|
@ -21,7 +21,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
|
|||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private userValidatorsService: UserValidatorsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -53,7 +53,7 @@ export class MyAccountProfileComponent extends FormReactive implements OnInit {
|
|||
this.user.account.displayName = displayName
|
||||
this.user.account.description = description
|
||||
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Profile updated.'))
|
||||
this.notifier.success(this.i18n('Profile updated.'))
|
||||
},
|
||||
|
||||
err => this.error = err.message
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
<span i18n class="user-quota-label">Video quota:</span> {{ userVideoQuotaUsed | bytes: 0 }} / {{ userVideoQuota }}
|
||||
</div>
|
||||
|
||||
<ng-template [ngIf]="user && user.account">
|
||||
<div i18n class="account-title">Profile</div>
|
||||
<my-account-profile [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-profile>
|
||||
</ng-template>
|
||||
|
||||
<div i18n class="account-title" id="notifications">Notifications</div>
|
||||
<my-account-notification-preferences [user]="user" [userInformationLoaded]="userInformationLoaded"></my-account-notification-preferences>
|
||||
|
||||
<div i18n class="account-title">Password</div>
|
||||
<my-account-change-password></my-account-change-password>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { BytesPipe } from 'ngx-pipes'
|
||||
import { AuthService } from '../../core'
|
||||
import { User } from '../../shared'
|
||||
|
@ -19,7 +19,7 @@ export class MyAccountSettingsComponent implements OnInit {
|
|||
constructor (
|
||||
private userService: UserService,
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private i18n: I18n
|
||||
) {}
|
||||
|
||||
|
@ -48,12 +48,12 @@ export class MyAccountSettingsComponent implements OnInit {
|
|||
this.userService.changeAvatar(formData)
|
||||
.subscribe(
|
||||
data => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Avatar changed.'))
|
||||
this.notifier.success(this.i18n('Avatar changed.'))
|
||||
|
||||
this.user.updateAccountAvatar(data.avatar)
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, Input, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { UserUpdateMe } from '../../../../../../shared'
|
||||
import { AuthService } from '../../../core'
|
||||
import { FormReactive, User, UserService } from '../../../shared'
|
||||
|
@ -19,7 +19,7 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
constructor (
|
||||
protected formValidatorService: FormValidatorService,
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -54,12 +54,12 @@ export class MyAccountVideoSettingsComponent extends FormReactive implements OnI
|
|||
|
||||
this.userService.updateMyProfile(details).subscribe(
|
||||
() => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Information updated.'))
|
||||
this.notifier.success(this.i18n('Information updated.'))
|
||||
|
||||
this.authService.refreshUserInformation()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { UserSubscriptionService } from '@app/shared/user-subscription'
|
||||
|
@ -21,7 +21,7 @@ export class MyAccountSubscriptionsComponent implements OnInit {
|
|||
|
||||
constructor (
|
||||
private userSubscriptionService: UserSubscriptionService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private i18n: I18n
|
||||
) {}
|
||||
|
||||
|
@ -37,7 +37,7 @@ export class MyAccountSubscriptionsComponent implements OnInit {
|
|||
this.pagination.totalItems = res.total
|
||||
},
|
||||
|
||||
error => this.notificationsService.error(this.i18n('Error'), error.message)
|
||||
error => this.notifier.error(error.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService, Notifier } from '@app/core'
|
||||
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
|
||||
import { VideoChannelCreate } from '../../../../../shared/models/videos'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
import { AuthService } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { VideoChannelValidatorsService } from '@app/shared/forms/form-validators/video-channel-validators.service'
|
||||
|
@ -21,7 +20,7 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
|
|||
protected formValidatorService: FormValidatorService,
|
||||
private authService: AuthService,
|
||||
private videoChannelValidatorsService: VideoChannelValidatorsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private router: Router,
|
||||
private videoChannelService: VideoChannelService,
|
||||
private i18n: I18n
|
||||
|
@ -56,8 +55,8 @@ export class MyAccountVideoChannelCreateComponent extends MyAccountVideoChannelE
|
|||
this.videoChannelService.createVideoChannel(videoChannelCreate).subscribe(
|
||||
() => {
|
||||
this.authService.refreshUserInformation()
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
|
||||
this.notifier.success(
|
||||
this.i18n('Video channel {{videoChannelName}} created.', { videoChannelName: videoChannelCreate.displayName })
|
||||
)
|
||||
this.router.navigate([ '/my-account', 'video-channels' ])
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService, Notifier, ServerService } from '@app/core'
|
||||
import { MyAccountVideoChannelEdit } from './my-account-video-channel-edit'
|
||||
import { VideoChannelUpdate } from '../../../../../shared/models/videos'
|
||||
import { VideoChannelService } from '@app/shared/video-channel/video-channel.service'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { AuthService, ServerService } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { VideoChannelValidatorsService } from '@app/shared/forms/form-validators/video-channel-validators.service'
|
||||
|
@ -26,7 +25,7 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
|
|||
protected formValidatorService: FormValidatorService,
|
||||
private authService: AuthService,
|
||||
private videoChannelValidatorsService: VideoChannelValidatorsService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private videoChannelService: VideoChannelService,
|
||||
|
@ -79,10 +78,11 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
|
|||
this.videoChannelService.updateVideoChannel(this.videoChannelToUpdate.name, videoChannelUpdate).subscribe(
|
||||
() => {
|
||||
this.authService.refreshUserInformation()
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
|
||||
this.notifier.success(
|
||||
this.i18n('Video channel {{videoChannelName}} updated.', { videoChannelName: videoChannelUpdate.displayName })
|
||||
)
|
||||
|
||||
this.router.navigate([ '/my-account', 'video-channels' ])
|
||||
},
|
||||
|
||||
|
@ -94,12 +94,12 @@ export class MyAccountVideoChannelUpdateComponent extends MyAccountVideoChannelE
|
|||
this.videoChannelService.changeVideoChannelAvatar(this.videoChannelToUpdate.name, formData)
|
||||
.subscribe(
|
||||
data => {
|
||||
this.notificationsService.success(this.i18n('Success'), this.i18n('Avatar changed.'))
|
||||
this.notifier.success(this.i18n('Avatar changed.'))
|
||||
|
||||
this.videoChannelToUpdate.updateAvatar(data.avatar)
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="video-channels-header">
|
||||
<a class="create-button" routerLink="create">
|
||||
<span class="icon icon-add"></span>
|
||||
<my-global-icon iconName="add"></my-global-icon>
|
||||
<ng-container i18n>Create another video channel</ng-container>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import '_mixins';
|
||||
|
||||
.create-button {
|
||||
@include create-button('../../../assets/images/global/add.svg');
|
||||
@include create-button;
|
||||
}
|
||||
|
||||
/deep/ .action-button {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
|
@ -20,7 +20,7 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
|||
|
||||
constructor (
|
||||
private authService: AuthService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private confirmService: ConfirmService,
|
||||
private videoChannelService: VideoChannelService,
|
||||
private i18n: I18n
|
||||
|
@ -35,10 +35,14 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
|||
async deleteVideoChannel (videoChannel: VideoChannel) {
|
||||
const res = await this.confirmService.confirmWithInput(
|
||||
this.i18n(
|
||||
'Do you really want to delete {{videoChannelName}}? It will delete all videos uploaded in this channel too.',
|
||||
{ videoChannelName: videoChannel.displayName }
|
||||
'Do you really want to delete {{channelDisplayName}}? It will delete all videos uploaded in this channel, ' +
|
||||
'and you will not be able to create another channel with the same name ({{channelName}})!',
|
||||
{ channelDisplayName: videoChannel.displayName, channelName: videoChannel.name }
|
||||
),
|
||||
this.i18n(
|
||||
'Please type the display name of the video channel ({{displayName}}) to confirm',
|
||||
{ displayName: videoChannel.displayName }
|
||||
),
|
||||
this.i18n('Please type the name of the video channel to confirm'),
|
||||
videoChannel.displayName,
|
||||
this.i18n('Delete')
|
||||
)
|
||||
|
@ -46,15 +50,14 @@ export class MyAccountVideoChannelsComponent implements OnInit {
|
|||
|
||||
this.videoChannelService.removeVideoChannel(videoChannel)
|
||||
.subscribe(
|
||||
status => {
|
||||
() => {
|
||||
this.loadVideoChannels()
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.notifier.success(
|
||||
this.i18n('Video channel {{videoChannelName}} deleted.', { videoChannelName: videoChannel.displayName })
|
||||
)
|
||||
},
|
||||
|
||||
error => this.notificationsService.error(this.i18n('Error'), error.message)
|
||||
error => this.notifier.error(error.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { RestPagination, RestTable } from '@app/shared'
|
||||
import { SortMeta } from 'primeng/components/common/sortmeta'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { VideoImport, VideoImportState } from '../../../../../shared/models/videos'
|
||||
import { VideoImportService } from '@app/shared/video-import'
|
||||
|
@ -19,7 +19,7 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
|
|||
pagination: RestPagination = { count: this.rowsPerPage, start: 0 }
|
||||
|
||||
constructor (
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private videoImportService: VideoImportService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -58,7 +58,7 @@ export class MyAccountVideoImportsComponent extends RestTable implements OnInit
|
|||
this.totalRecords = resultList.total
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</span>
|
||||
|
||||
<span class="action-button action-button-delete-selection" (click)="deleteSelectedVideos()">
|
||||
<span class="icon icon-delete-white"></span>
|
||||
<my-global-icon iconName="delete"></my-global-icon>
|
||||
<ng-container i18n>Delete</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -45,7 +45,7 @@
|
|||
|
||||
<my-button i18n-label label="Change ownership"
|
||||
className="action-button-change-ownership"
|
||||
icon="icon-im-with-her"
|
||||
icon="im-with-her"
|
||||
(click)="changeOwnership($event, video)"
|
||||
></my-button>
|
||||
</div>
|
||||
|
|
|
@ -23,14 +23,11 @@
|
|||
.action-button-delete-selection {
|
||||
@include peertube-button;
|
||||
@include orange-button;
|
||||
@include button-with-icon(21px);
|
||||
|
||||
my-global-icon {
|
||||
@include apply-svg-color(#fff);
|
||||
}
|
||||
|
||||
.icon.icon-delete-white {
|
||||
@include icon(21px);
|
||||
|
||||
position: relative;
|
||||
top: -2px;
|
||||
background-image: url('../../../assets/images/global/delete-white.svg');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +94,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 800px) {
|
||||
@media screen and (max-width: $small-view) {
|
||||
.video {
|
||||
flex-direction: column;
|
||||
height: auto;
|
||||
|
|
|
@ -5,7 +5,7 @@ import { ActivatedRoute, Router } from '@angular/router'
|
|||
import { Location } from '@angular/common'
|
||||
import { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { ComponentPagination } from '@app/shared/rest/component-pagination.model'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
||||
|
@ -40,7 +40,7 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected authService: AuthService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected notifier: Notifier,
|
||||
protected location: Location,
|
||||
protected screenService: ScreenService,
|
||||
protected i18n: I18n,
|
||||
|
@ -102,16 +102,13 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
.pipe(concatAll())
|
||||
.subscribe(
|
||||
res => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length })
|
||||
)
|
||||
this.notifier.success(this.i18n('{{deleteLength}} videos deleted.', { deleteLength: toDeleteVideosIds.length }))
|
||||
|
||||
this.abortSelectionMode()
|
||||
this.reloadVideos()
|
||||
},
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -124,15 +121,12 @@ export class MyAccountVideosComponent extends AbstractVideoList implements OnIni
|
|||
|
||||
this.videoService.removeVideo(video.id)
|
||||
.subscribe(
|
||||
status => {
|
||||
this.notificationsService.success(
|
||||
this.i18n('Success'),
|
||||
this.i18n('Video {{videoName}} deleted.', { videoName: video.name })
|
||||
)
|
||||
() => {
|
||||
this.notifier.success(this.i18n('Video {{videoName}} deleted.', { videoName: video.name }))
|
||||
this.reloadVideos()
|
||||
},
|
||||
|
||||
error => this.notificationsService.error(this.i18n('Error'), error.message)
|
||||
error => this.notifier.error(error.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<ng-template #modal let-close="close" let-dismiss="dismiss">
|
||||
<div class="modal-header">
|
||||
<h4 i18n class="modal-title">Change ownership</h4>
|
||||
<span class="close" aria-label="Close" role="button" (click)="dismiss()"></span>
|
||||
|
||||
<my-global-icon iconName="cross" aria-label="Close" role="button" (click)="dismiss()"></my-global-icon>
|
||||
</div>
|
||||
|
||||
<div class="modal-body" [formGroup]="form">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap'
|
||||
import { FormReactive, UserService } from '../../../shared/index'
|
||||
import { Video } from '@app/shared/video/video.model'
|
||||
|
@ -25,7 +25,7 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
|
|||
protected formValidatorService: FormValidatorService,
|
||||
private videoChangeOwnershipValidatorsService: VideoChangeOwnershipValidatorsService,
|
||||
private videoOwnershipService: VideoOwnershipService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private userService: UserService,
|
||||
private modalService: NgbModal,
|
||||
private i18n: I18n
|
||||
|
@ -53,11 +53,9 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
|
|||
const query = event.query
|
||||
this.userService.autocomplete(query)
|
||||
.subscribe(
|
||||
usernames => {
|
||||
this.usernamePropositions = usernames
|
||||
},
|
||||
usernames => this.usernamePropositions = usernames,
|
||||
|
||||
err => this.notificationsService.error('Error', err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -67,9 +65,9 @@ export class VideoChangeOwnershipComponent extends FormReactive implements OnIni
|
|||
this.videoOwnershipService
|
||||
.changeOwnership(this.video.id, username)
|
||||
.subscribe(
|
||||
() => this.notificationsService.success(this.i18n('Success'), this.i18n('Ownership change request sent.')),
|
||||
() => this.notifier.success(this.i18n('Ownership change request sent.')),
|
||||
|
||||
err => this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
err => this.notifier.error(err.message)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,5 @@
|
|||
<div class="row">
|
||||
<div class="sub-menu">
|
||||
<a i18n routerLink="/my-account/settings" routerLinkActive="active" class="title-page">My settings</a>
|
||||
|
||||
<div ngbDropdown class="my-library">
|
||||
<span role="button" class="title-page" [ngClass]="{ active: libraryLabel !== '' }" ngbDropdownToggle>
|
||||
<ng-container i18n>My library</ng-container>
|
||||
<ng-container *ngIf="libraryLabel"> - {{ libraryLabel }}</ng-container>
|
||||
</span>
|
||||
|
||||
<div ngbDropdownMenu>
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/video-channels">My channels</a>
|
||||
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/videos">My videos</a>
|
||||
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/subscriptions">My subscriptions</a>
|
||||
|
||||
<a class="dropdown-item" *ngIf="isVideoImportEnabled()" i18n routerLink="/my-account/video-imports">My imports</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbDropdown class="misc">
|
||||
<span role="button" class="title-page" [ngClass]="{ active: miscLabel !== '' }" ngbDropdownToggle>
|
||||
<ng-container i18n>Misc</ng-container>
|
||||
<ng-container *ngIf="miscLabel"> - {{ miscLabel }}</ng-container>
|
||||
</span>
|
||||
|
||||
<div ngbDropdownMenu>
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/blocklist/accounts">Muted accounts</a>
|
||||
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/blocklist/servers">Muted instances</a>
|
||||
|
||||
<a class="dropdown-item" i18n routerLink="/my-account/ownership">Ownership changes</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<my-top-menu-dropdown [menuEntries]="menuEntries"></my-top-menu-dropdown>
|
||||
|
||||
<div class="margin-content">
|
||||
<router-outlet></router-outlet>
|
||||
|
|
|
@ -1,14 +1,3 @@
|
|||
.my-library, .misc {
|
||||
span[role=button] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
/deep/ .dropdown-toggle::after {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
.row {
|
||||
flex-direction: column;
|
||||
}
|
|
@ -1,38 +1,80 @@
|
|||
import { Component, OnDestroy, OnInit } from '@angular/core'
|
||||
import { Component } from '@angular/core'
|
||||
import { ServerService } from '@app/core'
|
||||
import { NavigationStart, Router } from '@angular/router'
|
||||
import { filter } from 'rxjs/operators'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { TopMenuDropdownParam } from '@app/shared/menu/top-menu-dropdown.component'
|
||||
|
||||
@Component({
|
||||
selector: 'my-my-account',
|
||||
templateUrl: './my-account.component.html',
|
||||
styleUrls: [ './my-account.component.scss' ]
|
||||
})
|
||||
export class MyAccountComponent implements OnInit, OnDestroy {
|
||||
|
||||
libraryLabel = ''
|
||||
miscLabel = ''
|
||||
|
||||
private routeSub: Subscription
|
||||
export class MyAccountComponent {
|
||||
menuEntries: TopMenuDropdownParam[] = []
|
||||
|
||||
constructor (
|
||||
private serverService: ServerService,
|
||||
private router: Router,
|
||||
private i18n: I18n
|
||||
) {}
|
||||
) {
|
||||
|
||||
ngOnInit () {
|
||||
this.updateLabels(this.router.url)
|
||||
|
||||
this.routeSub = this.router.events
|
||||
.pipe(filter(event => event instanceof NavigationStart))
|
||||
.subscribe((event: NavigationStart) => this.updateLabels(event.url))
|
||||
const libraryEntries: TopMenuDropdownParam = {
|
||||
label: this.i18n('My library'),
|
||||
children: [
|
||||
{
|
||||
label: this.i18n('My channels'),
|
||||
routerLink: '/my-account/video-channels'
|
||||
},
|
||||
{
|
||||
label: this.i18n('My videos'),
|
||||
routerLink: '/my-account/videos'
|
||||
},
|
||||
{
|
||||
label: this.i18n('My subscriptions'),
|
||||
routerLink: '/my-account/subscriptions'
|
||||
},
|
||||
{
|
||||
label: this.i18n('My history'),
|
||||
routerLink: '/my-account/history/videos'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
ngOnDestroy () {
|
||||
if (this.routeSub) this.routeSub.unsubscribe()
|
||||
if (this.isVideoImportEnabled()) {
|
||||
libraryEntries.children.push({
|
||||
label: 'My imports',
|
||||
routerLink: '/my-account/video-imports'
|
||||
})
|
||||
}
|
||||
|
||||
const miscEntries: TopMenuDropdownParam = {
|
||||
label: this.i18n('Misc'),
|
||||
children: [
|
||||
{
|
||||
label: this.i18n('Muted accounts'),
|
||||
routerLink: '/my-account/blocklist/accounts'
|
||||
},
|
||||
{
|
||||
label: this.i18n('Muted instances'),
|
||||
routerLink: '/my-account/blocklist/servers'
|
||||
},
|
||||
{
|
||||
label: this.i18n('Ownership changes'),
|
||||
routerLink: '/my-account/ownership'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.menuEntries = [
|
||||
{
|
||||
label: this.i18n('My settings'),
|
||||
routerLink: '/my-account/settings'
|
||||
},
|
||||
{
|
||||
label: this.i18n('My notifications'),
|
||||
routerLink: '/my-account/notifications'
|
||||
},
|
||||
libraryEntries,
|
||||
miscEntries
|
||||
]
|
||||
}
|
||||
|
||||
isVideoImportEnabled () {
|
||||
|
@ -41,27 +83,4 @@ export class MyAccountComponent implements OnInit, OnDestroy {
|
|||
return importConfig.http.enabled || importConfig.torrent.enabled
|
||||
}
|
||||
|
||||
private updateLabels (url: string) {
|
||||
const [ path ] = url.split('?')
|
||||
|
||||
if (path.startsWith('/my-account/video-channels')) {
|
||||
this.libraryLabel = this.i18n('Channels')
|
||||
} else if (path.startsWith('/my-account/videos')) {
|
||||
this.libraryLabel = this.i18n('Videos')
|
||||
} else if (path.startsWith('/my-account/subscriptions')) {
|
||||
this.libraryLabel = this.i18n('Subscriptions')
|
||||
} else if (path.startsWith('/my-account/video-imports')) {
|
||||
this.libraryLabel = this.i18n('Video imports')
|
||||
} else {
|
||||
this.libraryLabel = ''
|
||||
}
|
||||
|
||||
if (path.startsWith('/my-account/blocklist/accounts')) {
|
||||
this.miscLabel = this.i18n('Muted accounts')
|
||||
} else if (path.startsWith('/my-account/blocklist/servers')) {
|
||||
this.miscLabel = this.i18n('Muted instances')
|
||||
} else {
|
||||
this.miscLabel = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { TableModule } from 'primeng/table'
|
||||
import { NgModule } from '@angular/core'
|
||||
import { AutoCompleteModule } from 'primeng/autocomplete'
|
||||
import { InputSwitchModule } from 'primeng/inputswitch'
|
||||
import { SharedModule } from '../shared'
|
||||
import { MyAccountRoutingModule } from './my-account-routing.module'
|
||||
import { MyAccountChangePasswordComponent } from './my-account-settings/my-account-change-password/my-account-change-password.component'
|
||||
|
@ -21,6 +22,9 @@ import { MyAccountDangerZoneComponent } from '@app/+my-account/my-account-settin
|
|||
import { MyAccountSubscriptionsComponent } from '@app/+my-account/my-account-subscriptions/my-account-subscriptions.component'
|
||||
import { MyAccountBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-blocklist.component'
|
||||
import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-blocklist/my-account-server-blocklist.component'
|
||||
import { MyAccountHistoryComponent } from '@app/+my-account/my-account-history/my-account-history.component'
|
||||
import { MyAccountNotificationsComponent } from '@app/+my-account/my-account-notifications/my-account-notifications.component'
|
||||
import { MyAccountNotificationPreferencesComponent } from '@app/+my-account/my-account-settings/my-account-notification-preferences'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -28,7 +32,8 @@ import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-b
|
|||
MyAccountRoutingModule,
|
||||
AutoCompleteModule,
|
||||
SharedModule,
|
||||
TableModule
|
||||
TableModule,
|
||||
InputSwitchModule
|
||||
],
|
||||
|
||||
declarations: [
|
||||
|
@ -49,7 +54,10 @@ import { MyAccountServerBlocklistComponent } from '@app/+my-account/my-account-b
|
|||
MyAccountDangerZoneComponent,
|
||||
MyAccountSubscriptionsComponent,
|
||||
MyAccountBlocklistComponent,
|
||||
MyAccountServerBlocklistComponent
|
||||
MyAccountServerBlocklistComponent,
|
||||
MyAccountHistoryComponent,
|
||||
MyAccountNotificationsComponent,
|
||||
MyAccountNotificationPreferencesComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'
|
||||
import { ServerService } from '../../core/server'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { Account } from '@app/shared/account/account.model'
|
||||
import { Notifier } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-actor-avatar-info',
|
||||
|
@ -18,13 +18,13 @@ export class ActorAvatarInfoComponent {
|
|||
|
||||
constructor (
|
||||
private serverService: ServerService,
|
||||
private notificationsService: NotificationsService
|
||||
private notifier: Notifier
|
||||
) {}
|
||||
|
||||
onAvatarChange () {
|
||||
const avatarfile = this.avatarfileInput.nativeElement.files[ 0 ]
|
||||
if (avatarfile.size > this.maxAvatarSize) {
|
||||
this.notificationsService.error('Error', 'This image is too large.')
|
||||
this.notifier.error('Error', 'This image is too large.')
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier, RedirectService } from '@app/core'
|
||||
import { ServerService } from '@app/core/server'
|
||||
import { RedirectService } from '@app/core'
|
||||
import { UserService, FormReactive } from '@app/shared'
|
||||
import { FormReactive, UserService } from '@app/shared'
|
||||
import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service'
|
||||
import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service'
|
||||
|
||||
|
@ -20,7 +19,7 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
|
|||
private userValidatorsService: UserValidatorsService,
|
||||
private userService: UserService,
|
||||
private serverService: ServerService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private redirectService: RedirectService,
|
||||
private i18n: I18n
|
||||
) {
|
||||
|
@ -46,12 +45,12 @@ export class VerifyAccountAskSendEmailComponent extends FormReactive implements
|
|||
'An email with verification link will be sent to {{email}}.',
|
||||
{ email }
|
||||
)
|
||||
this.notificationsService.success(this.i18n('Success'), message)
|
||||
this.notifier.success(message)
|
||||
this.redirectService.redirectToHomepage()
|
||||
},
|
||||
|
||||
err => {
|
||||
this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
this.notifier.error(err.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<ng-template #verificationError>
|
||||
<div>
|
||||
<span i18n>An error occurred. </span>
|
||||
<a i18n routerLink="/verify-account/ask-email">Request new verification email.</a>
|
||||
<a i18n routerLink="/verify-account/ask-send-email">Request new verification email.</a>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Component, OnInit } from '@angular/core'
|
||||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core'
|
||||
import { UserService } from '@app/shared'
|
||||
|
||||
@Component({
|
||||
|
@ -17,7 +17,7 @@ export class VerifyAccountEmailComponent implements OnInit {
|
|||
|
||||
constructor (
|
||||
private userService: UserService,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private router: Router,
|
||||
private route: ActivatedRoute,
|
||||
private i18n: I18n
|
||||
|
@ -29,7 +29,7 @@ export class VerifyAccountEmailComponent implements OnInit {
|
|||
this.verificationString = this.route.snapshot.queryParams['verificationString']
|
||||
|
||||
if (!this.userId || !this.verificationString) {
|
||||
this.notificationsService.error(this.i18n('Error'), this.i18n('Unable to find user id or verification string.'))
|
||||
this.notifier.error(this.i18n('Unable to find user id or verification string.'))
|
||||
} else {
|
||||
this.verifyEmail()
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export class VerifyAccountEmailComponent implements OnInit {
|
|||
},
|
||||
|
||||
err => {
|
||||
this.notificationsService.error(this.i18n('Error'), err.message)
|
||||
this.notifier.error(err.message)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { VideoChannelService } from '@app/shared/video-channel/video-channel.ser
|
|||
import { VideoChannel } from '@app/shared/video-channel/video-channel.model'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { MarkdownService } from '@app/videos/shared'
|
||||
import { MarkdownService } from '@app/shared/renderer'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-channel-about',
|
||||
|
|
|
@ -2,7 +2,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'
|
|||
import { ActivatedRoute, Router } from '@angular/router'
|
||||
import { Location } from '@angular/common'
|
||||
import { immutableAssign } from '@app/shared/misc/utils'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { AuthService } from '../../core/auth'
|
||||
import { ConfirmService } from '../../core/confirm'
|
||||
import { AbstractVideoList } from '../../shared/video/abstract-video-list'
|
||||
|
@ -13,6 +12,7 @@ import { tap } from 'rxjs/operators'
|
|||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { Subscription } from 'rxjs'
|
||||
import { ScreenService } from '@app/shared/misc/screen.service'
|
||||
import { Notifier } from '@app/core'
|
||||
|
||||
@Component({
|
||||
selector: 'my-video-channel-videos',
|
||||
|
@ -35,7 +35,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
|
|||
protected router: Router,
|
||||
protected route: ActivatedRoute,
|
||||
protected authService: AuthService,
|
||||
protected notificationsService: NotificationsService,
|
||||
protected notifier: Notifier,
|
||||
protected confirmService: ConfirmService,
|
||||
protected location: Location,
|
||||
protected screenService: ScreenService,
|
||||
|
@ -55,7 +55,7 @@ export class VideoChannelVideosComponent extends AbstractVideoList implements On
|
|||
this.videoChannelSub = this.videoChannelService.videoChannelLoaded
|
||||
.subscribe(videoChannel => {
|
||||
this.videoChannel = videoChannel
|
||||
this.currentRoute = '/video-channels/' + this.videoChannel.uuid + '/videos'
|
||||
this.currentRoute = '/video-channels/' + this.videoChannel.nameWithHost + '/videos'
|
||||
|
||||
this.reloadVideos()
|
||||
this.generateSyndicationList()
|
||||
|
|
|
@ -7,7 +7,7 @@ import { VideoChannelAboutComponent } from './video-channel-about/video-channel-
|
|||
|
||||
const videoChannelsRoutes: Routes = [
|
||||
{
|
||||
path: ':videoChannelId',
|
||||
path: ':videoChannelName',
|
||||
component: VideoChannelsComponent,
|
||||
canActivateChild: [ MetaGuard ],
|
||||
children: [
|
||||
|
|
|
@ -34,9 +34,9 @@ export class VideoChannelsComponent implements OnInit, OnDestroy {
|
|||
ngOnInit () {
|
||||
this.routeSub = this.route.params
|
||||
.pipe(
|
||||
map(params => params[ 'videoChannelId' ]),
|
||||
map(params => params[ 'videoChannelName' ]),
|
||||
distinctUntilChanged(),
|
||||
switchMap(videoChannelId => this.videoChannelService.getVideoChannel(videoChannelId)),
|
||||
switchMap(videoChannelName => this.videoChannelService.getVideoChannel(videoChannelName)),
|
||||
catchError(err => this.restExtractor.redirectTo404IfNotFound(err, [ 400, 404 ]))
|
||||
)
|
||||
.subscribe(videoChannel => this.videoChannel = videoChannel)
|
||||
|
|
|
@ -43,7 +43,8 @@ const routes: Routes = [
|
|||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
useHash: Boolean(history.pushState) === false,
|
||||
preloadingStrategy: PreloadSelectedModulesList
|
||||
preloadingStrategy: PreloadSelectedModulesList,
|
||||
anchorScrolling: 'enabled'
|
||||
})
|
||||
],
|
||||
providers: [
|
||||
|
|
|
@ -30,12 +30,27 @@
|
|||
|
||||
<footer class="row">
|
||||
<a href="https://joinpeertube.org" title="PeerTube website" target="_blank" rel="noopener noreferrer">PeerTube v{{ serverVersion }}{{ serverCommit }}</a> -
|
||||
<a href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE" title="PeerTube license" target="_blank" rel="noopener noreferrer">CopyLeft 2015-2018</a>
|
||||
<a href="https://github.com/Chocobozzz/PeerTube/blob/develop/LICENSE" title="PeerTube license" target="_blank" rel="noopener noreferrer">CopyLeft 2015-2019</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ngx-loading-bar [includeSpinner]="false"></ngx-loading-bar>
|
||||
|
||||
<my-confirm></my-confirm>
|
||||
<simple-notifications [options]="notificationOptions"></simple-notifications>
|
||||
|
||||
<p-toast position="bottom-right">
|
||||
<ng-template let-message pTemplate="message">
|
||||
<div class="notification-block">
|
||||
<div class="message">
|
||||
<h3>{{ message.summary }}</h3>
|
||||
<p>{{ message.detail }}</p>
|
||||
</div>
|
||||
|
||||
<span *ngIf="message.severity === 'success'" class="glyphicon glyphicon-ok"></span>
|
||||
<span *ngIf="message.severity === 'info'" class="glyphicon glyphicon-info-sign"></span>
|
||||
<span *ngIf="message.severity === 'error'" class="glyphicon glyphicon-remove"></span>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-toast>
|
||||
|
|
|
@ -91,8 +91,3 @@ footer {
|
|||
height: $footer-height;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
simple-notifications {
|
||||
position: relative;
|
||||
z-index: 1500;
|
||||
}
|
||||
|
|
|
@ -15,19 +15,6 @@ import { fromEvent } from 'rxjs'
|
|||
styleUrls: [ './app.component.scss' ]
|
||||
})
|
||||
export class AppComponent implements OnInit {
|
||||
notificationOptions = {
|
||||
timeOut: 5000,
|
||||
lastOnBottom: true,
|
||||
clickToClose: true,
|
||||
maxLength: 0,
|
||||
maxStack: 7,
|
||||
showProgressBar: false,
|
||||
pauseOnHover: false,
|
||||
preventDuplicates: false,
|
||||
preventLastDuplicates: 'visible',
|
||||
rtl: false
|
||||
}
|
||||
|
||||
isMenuDisplayed = true
|
||||
isMenuChangedByUser = false
|
||||
|
||||
|
|
|
@ -12,13 +12,12 @@ import { AppComponent } from './app.component'
|
|||
import { CoreModule } from './core'
|
||||
import { HeaderComponent } from './header'
|
||||
import { LoginModule } from './login'
|
||||
import { MenuComponent } from './menu'
|
||||
import { AvatarNotificationComponent, LanguageChooserComponent, MenuComponent } from './menu'
|
||||
import { SharedModule } from './shared'
|
||||
import { SignupModule } from './signup'
|
||||
import { VideosModule } from './videos'
|
||||
import { buildFileLocale, getCompleteLocale, isDefaultLocale } from '../../../shared/models/i18n'
|
||||
import { getDevLocale, isOnDevLocale } from '@app/shared/i18n/i18n-utils'
|
||||
import { LanguageChooserComponent } from '@app/menu/language-chooser.component'
|
||||
import { SearchModule } from '@app/search'
|
||||
|
||||
export function metaFactory (serverService: ServerService): MetaLoader {
|
||||
|
@ -40,6 +39,7 @@ export function metaFactory (serverService: ServerService): MetaLoader {
|
|||
|
||||
MenuComponent,
|
||||
LanguageChooserComponent,
|
||||
AvatarNotificationComponent,
|
||||
HeaderComponent
|
||||
],
|
||||
imports: [
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
|
||||
import { UserRight } from '../../../../../shared/models/users/user-right.enum'
|
||||
import { User as ServerUserModel } from '../../../../../shared/models/users/user.model'
|
||||
// Do not use the barrel (dependency loop)
|
||||
import { hasUserRight, UserRole } from '../../../../../shared/models/users/user-role'
|
||||
import { User, UserConstructorHash } from '../../shared/users/user.model'
|
||||
import { User } from '../../shared/users/user.model'
|
||||
import { NSFWPolicyType } from '../../../../../shared/models/videos/nsfw-policy.type'
|
||||
|
||||
export type TokenOptions = {
|
||||
|
@ -70,6 +71,7 @@ export class AuthUser extends User {
|
|||
ID: 'id',
|
||||
ROLE: 'role',
|
||||
EMAIL: 'email',
|
||||
VIDEOS_HISTORY_ENABLED: 'videos-history-enabled',
|
||||
USERNAME: 'username',
|
||||
NSFW_POLICY: 'nsfw_policy',
|
||||
WEBTORRENT_ENABLED: 'peertube-videojs-' + 'webtorrent_enabled',
|
||||
|
@ -89,7 +91,8 @@ export class AuthUser extends User {
|
|||
role: parseInt(peertubeLocalStorage.getItem(this.KEYS.ROLE), 10) as UserRole,
|
||||
nsfwPolicy: peertubeLocalStorage.getItem(this.KEYS.NSFW_POLICY) as NSFWPolicyType,
|
||||
webTorrentEnabled: peertubeLocalStorage.getItem(this.KEYS.WEBTORRENT_ENABLED) === 'true',
|
||||
autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true'
|
||||
autoPlayVideo: peertubeLocalStorage.getItem(this.KEYS.AUTO_PLAY_VIDEO) === 'true',
|
||||
videosHistoryEnabled: peertubeLocalStorage.getItem(this.KEYS.VIDEOS_HISTORY_ENABLED) === 'true'
|
||||
},
|
||||
Tokens.load()
|
||||
)
|
||||
|
@ -104,12 +107,13 @@ export class AuthUser extends User {
|
|||
peertubeLocalStorage.removeItem(this.KEYS.ROLE)
|
||||
peertubeLocalStorage.removeItem(this.KEYS.NSFW_POLICY)
|
||||
peertubeLocalStorage.removeItem(this.KEYS.WEBTORRENT_ENABLED)
|
||||
peertubeLocalStorage.removeItem(this.KEYS.VIDEOS_HISTORY_ENABLED)
|
||||
peertubeLocalStorage.removeItem(this.KEYS.AUTO_PLAY_VIDEO)
|
||||
peertubeLocalStorage.removeItem(this.KEYS.EMAIL)
|
||||
Tokens.flush()
|
||||
}
|
||||
|
||||
constructor (userHash: UserConstructorHash, hashTokens: TokenOptions) {
|
||||
constructor (userHash: Partial<ServerUserModel>, hashTokens: TokenOptions) {
|
||||
super(userHash)
|
||||
this.tokens = new Tokens(hashTokens)
|
||||
}
|
||||
|
|
|
@ -3,18 +3,18 @@ import { catchError, map, mergeMap, share, tap } from 'rxjs/operators'
|
|||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http'
|
||||
import { Injectable } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NotificationsService } from 'angular2-notifications'
|
||||
import { Notifier } from '@app/core/notification/notifier.service'
|
||||
import { OAuthClientLocal, User as UserServerModel, UserRefreshToken } from '../../../../../shared'
|
||||
import { User } from '../../../../../shared/models/users'
|
||||
import { UserLogin } from '../../../../../shared/models/users/user-login.model'
|
||||
import { environment } from '../../../environments/environment'
|
||||
import { RestExtractor } from '../../shared/rest'
|
||||
import { RestExtractor } from '../../shared/rest/rest-extractor.service'
|
||||
import { AuthStatus } from './auth-status.model'
|
||||
import { AuthUser } from './auth-user.model'
|
||||
import { objectToUrlEncoded } from '@app/shared/misc/utils'
|
||||
import { peertubeLocalStorage } from '@app/shared/misc/peertube-local-storage'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
import { HotkeysService, Hotkey } from 'angular2-hotkeys'
|
||||
import { Hotkey, HotkeysService } from 'angular2-hotkeys'
|
||||
|
||||
interface UserLoginWithUsername extends UserLogin {
|
||||
access_token: string
|
||||
|
@ -38,7 +38,6 @@ export class AuthService {
|
|||
loginChangedSource: Observable<AuthStatus>
|
||||
userInformationLoaded = new ReplaySubject<boolean>(1)
|
||||
hotkeys: Hotkey[]
|
||||
redirectUrl: string
|
||||
|
||||
private clientId: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_ID)
|
||||
private clientSecret: string = peertubeLocalStorage.getItem(AuthService.LOCAL_STORAGE_OAUTH_CLIENT_KEYS.CLIENT_SECRET)
|
||||
|
@ -48,7 +47,7 @@ export class AuthService {
|
|||
|
||||
constructor (
|
||||
private http: HttpClient,
|
||||
private notificationsService: NotificationsService,
|
||||
private notifier: Notifier,
|
||||
private hotkeysService: HotkeysService,
|
||||
private restExtractor: RestExtractor,
|
||||
private router: Router,
|
||||
|
@ -106,9 +105,8 @@ export class AuthService {
|
|||
)
|
||||
}
|
||||
|
||||
// We put a bigger timeout
|
||||
// This is an important message
|
||||
this.notificationsService.error(this.i18n('Error'), errorMessage, { timeOut: 7000 })
|
||||
// We put a bigger timeout: this is an important message
|
||||
this.notifier.error(errorMessage, this.i18n('Error'), 7000)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -178,8 +176,6 @@ export class AuthService {
|
|||
this.setStatus(AuthStatus.LoggedOut)
|
||||
|
||||
this.hotkeysService.remove(this.hotkeys)
|
||||
|
||||
this.redirectUrl = null
|
||||
}
|
||||
|
||||
refreshAccessToken () {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
export * from './auth-status.model'
|
||||
export * from './auth-user.model'
|
||||
export * from './auth.service'
|
||||
export * from '../routing/login-guard.service'
|
||||
|
|
|
@ -1,2 +1 @@
|
|||
export * from './confirm.component'
|
||||
export * from './confirm.service'
|
||||
|
|
|
@ -7,16 +7,18 @@ import { LoadingBarModule } from '@ngx-loading-bar/core'
|
|||
import { LoadingBarHttpClientModule } from '@ngx-loading-bar/http-client'
|
||||
import { LoadingBarRouterModule } from '@ngx-loading-bar/router'
|
||||
|
||||
import { SimpleNotificationsModule } from 'angular2-notifications'
|
||||
|
||||
import { AuthService } from './auth'
|
||||
import { ConfirmComponent, ConfirmService } from './confirm'
|
||||
import { ConfirmService } from './confirm'
|
||||
import { throwIfAlreadyLoaded } from './module-import-guard'
|
||||
import { LoginGuard, RedirectService, UserRightGuard } from './routing'
|
||||
import { ServerService } from './server'
|
||||
import { ThemeService } from './theme'
|
||||
import { HotkeyModule } from 'angular2-hotkeys'
|
||||
import { CheatSheetComponent } from '@app/core/hotkeys'
|
||||
import { CheatSheetComponent } from './hotkeys'
|
||||
import { ToastModule } from 'primeng/toast'
|
||||
import { Notifier } from './notification'
|
||||
import { MessageService } from 'primeng/api'
|
||||
import { UserNotificationSocket } from '@app/core/notification/user-notification-socket.service'
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
|
@ -25,11 +27,10 @@ import { CheatSheetComponent } from '@app/core/hotkeys'
|
|||
FormsModule,
|
||||
BrowserAnimationsModule,
|
||||
|
||||
SimpleNotificationsModule.forRoot(),
|
||||
|
||||
LoadingBarHttpClientModule,
|
||||
LoadingBarRouterModule,
|
||||
LoadingBarModule.forRoot(),
|
||||
LoadingBarModule,
|
||||
ToastModule,
|
||||
|
||||
HotkeyModule.forRoot({
|
||||
cheatSheetCloseEsc: true
|
||||
|
@ -37,16 +38,15 @@ import { CheatSheetComponent } from '@app/core/hotkeys'
|
|||
],
|
||||
|
||||
declarations: [
|
||||
ConfirmComponent,
|
||||
CheatSheetComponent
|
||||
],
|
||||
|
||||
exports: [
|
||||
SimpleNotificationsModule,
|
||||
LoadingBarHttpClientModule,
|
||||
LoadingBarModule,
|
||||
|
||||
ConfirmComponent,
|
||||
ToastModule,
|
||||
|
||||
CheatSheetComponent
|
||||
],
|
||||
|
||||
|
@ -57,7 +57,10 @@ import { CheatSheetComponent } from '@app/core/hotkeys'
|
|||
ThemeService,
|
||||
LoginGuard,
|
||||
UserRightGuard,
|
||||
RedirectService
|
||||
RedirectService,
|
||||
Notifier,
|
||||
MessageService,
|
||||
UserNotificationSocket
|
||||
]
|
||||
})
|
||||
export class CoreModule {
|
||||
|
|
|
@ -2,6 +2,7 @@ export * from './auth'
|
|||
export * from './confirm'
|
||||
export * from './routing'
|
||||
export * from './server'
|
||||
export * from './notification'
|
||||
export * from './theme'
|
||||
|
||||
export * from './core.module'
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
export * from './notifier.service'
|
||||
export * from './user-notification-socket.service'
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { MessageService } from 'primeng/api'
|
||||
import { I18n } from '@ngx-translate/i18n-polyfill'
|
||||
|
||||
@Injectable()
|
||||
export class Notifier {
|
||||
readonly TIMEOUT = 5000
|
||||
|
||||
constructor (
|
||||
private i18n: I18n,
|
||||
private messageService: MessageService) {
|
||||
}
|
||||
|
||||
info (text: string, title?: string, timeout?: number) {
|
||||
if (!title) title = this.i18n('Info')
|
||||
|
||||
return this.notify('info', text, title, timeout)
|
||||
}
|
||||
|
||||
error (text: string, title?: string, timeout?: number) {
|
||||
if (!title) title = this.i18n('Error')
|
||||
|
||||
return this.notify('error', text, title, timeout)
|
||||
}
|
||||
|
||||
success (text: string, title?: string, timeout?: number) {
|
||||
if (!title) title = this.i18n('Success')
|
||||
|
||||
return this.notify('success', text, title, timeout)
|
||||
}
|
||||
|
||||
private notify (severity: 'success' | 'info' | 'warn' | 'error', text: string, title: string, timeout?: number) {
|
||||
this.messageService.add({
|
||||
severity,
|
||||
summary: title,
|
||||
detail: text,
|
||||
closable: true,
|
||||
life: timeout || this.TIMEOUT
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { environment } from '../../../environments/environment'
|
||||
import { UserNotification as UserNotificationServer } from '../../../../../shared'
|
||||
import { Subject } from 'rxjs'
|
||||
import * as io from 'socket.io-client'
|
||||
import { AuthService } from '../auth'
|
||||
|
||||
export type NotificationEvent = 'new' | 'read' | 'read-all'
|
||||
|
||||
@Injectable()
|
||||
export class UserNotificationSocket {
|
||||
private notificationSubject = new Subject<{ type: NotificationEvent, notification?: UserNotificationServer }>()
|
||||
|
||||
private socket: SocketIOClient.Socket
|
||||
|
||||
constructor (
|
||||
private auth: AuthService
|
||||
) {}
|
||||
|
||||
dispatch (type: NotificationEvent, notification?: UserNotificationServer) {
|
||||
this.notificationSubject.next({ type, notification })
|
||||
}
|
||||
|
||||
getMyNotificationsSocket () {
|
||||
const socket = this.getSocket()
|
||||
|
||||
socket.on('new-notification', (n: UserNotificationServer) => this.dispatch('new', n))
|
||||
|
||||
return this.notificationSubject.asObservable()
|
||||
}
|
||||
|
||||
private getSocket () {
|
||||
if (this.socket) return this.socket
|
||||
|
||||
this.socket = io(environment.apiUrl + '/user-notifications', {
|
||||
query: { accessToken: this.auth.getAccessToken() }
|
||||
})
|
||||
|
||||
return this.socket
|
||||
}
|
||||
}
|
|
@ -1,11 +1,5 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import {
|
||||
ActivatedRouteSnapshot,
|
||||
CanActivateChild,
|
||||
RouterStateSnapshot,
|
||||
CanActivate,
|
||||
Router
|
||||
} from '@angular/router'
|
||||
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot } from '@angular/router'
|
||||
|
||||
import { AuthService } from '../auth/auth.service'
|
||||
|
||||
|
@ -20,8 +14,6 @@ export class LoginGuard implements CanActivate, CanActivateChild {
|
|||
canActivate (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
|
||||
if (this.auth.isLoggedIn() === true) return true
|
||||
|
||||
this.auth.redirectUrl = state.url
|
||||
|
||||
this.router.navigate([ '/login' ])
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Injectable } from '@angular/core'
|
||||
import { Router } from '@angular/router'
|
||||
import { NavigationEnd, Router } from '@angular/router'
|
||||
import { ServerService } from '../server'
|
||||
|
||||
@Injectable()
|
||||
|
@ -8,6 +8,9 @@ export class RedirectService {
|
|||
static INIT_DEFAULT_ROUTE = '/videos/trending'
|
||||
static DEFAULT_ROUTE = RedirectService.INIT_DEFAULT_ROUTE
|
||||
|
||||
private previousUrl: string
|
||||
private currentUrl: string
|
||||
|
||||
constructor (
|
||||
private router: Router,
|
||||
private serverService: ServerService
|
||||
|
@ -18,6 +21,7 @@ export class RedirectService {
|
|||
RedirectService.DEFAULT_ROUTE = config.instance.defaultClientRoute
|
||||
}
|
||||
|
||||
// Load default route
|
||||
this.serverService.configLoaded
|
||||
.subscribe(() => {
|
||||
const defaultRouteConfig = this.serverService.getConfig().instance.defaultClientRoute
|
||||
|
@ -26,6 +30,21 @@ export class RedirectService {
|
|||
RedirectService.DEFAULT_ROUTE = defaultRouteConfig
|
||||
}
|
||||
})
|
||||
|
||||
// Track previous url
|
||||
this.currentUrl = this.router.url
|
||||
router.events.subscribe(event => {
|
||||
if (event instanceof NavigationEnd) {
|
||||
this.previousUrl = this.currentUrl
|
||||
this.currentUrl = event.url
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
redirectToPreviousRoute () {
|
||||
if (this.previousUrl) return this.router.navigateByUrl(this.previousUrl)
|
||||
|
||||
return this.redirectToHomepage()
|
||||
}
|
||||
|
||||
redirectToHomepage (skipLocationChange = false) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue