Merge pull request #310 from NicolasConstant/develop

0.31.0 PR
This commit is contained in:
Nicolas Constant 2020-08-29 22:54:02 +02:00 committed by GitHub
commit 4df59f0edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 117 additions and 54 deletions

View File

@ -22,7 +22,7 @@ Sengi already supporting all the basics functionalities, but many minors enhanc
## Screens
![/docs/images/presentation_small.gif](/docs/images/presentation_small.gif)
![https://raw.githubusercontent.com/NicolasConstant/sengi/master/docs/images/presentation_small.gif](https://raw.githubusercontent.com/NicolasConstant/sengi/master/docs/images/presentation_small.gif)
## Contact
@ -36,21 +36,21 @@ It's a little [elephant shrew](https://en.wikipedia.org/wiki/Elephant_shrew) fro
## Contribute
Please see the [contributing guidelines](CONTRIBUTING.md)
Please see the [contributing guidelines](https://github.com/NicolasConstant/sengi/blob/master/CONTRIBUTING.md)
## License
This project is licensed under the AGPLv3 License - see [LICENSE](LICENSE) for details
This project is licensed under the AGPLv3 License - see [LICENSE](https://github.com/NicolasConstant/sengi/blob/master/LICENSE) for details
## Credits
See [credits](CREDITS.md)
See [credits](https://github.com/NicolasConstant/sengi/blob/master/CREDITS.md)
## Dependencies
* [Angular 7](https://github.com/angular/angular)
* [NGXS](https://github.com/ngxs/store)
* [SASS](https://github.com/sass/dart-sass)
* [Electron 4](https://github.com/electron/electron)
* [Electron 8](https://github.com/electron/electron)

View File

@ -1,6 +1,6 @@
{
"name": "sengi",
"version": "0.30.1",
"version": "0.31.0",
"license": "AGPL-3.0-or-later",
"main": "main-electron.js",
"description": "A multi-account desktop client for Mastodon and Pleroma",

View File

@ -663,7 +663,7 @@ export class CreateStatusComponent implements OnInit, OnDestroy {
private getLinksExtraChars(status: string): number {
let mentionExtraChars = 0;
let links = status.split(' ').filter(x => x.startsWith('http://') || x.startsWith('https://'));
let links = status.split(/\s+/).filter(x => x.startsWith('http://') || x.startsWith('https://'));
for (let link of links) {
if (link.length > 23) {
mentionExtraChars += link.length - 23;

View File

@ -58,6 +58,11 @@ export class BookmarksComponent extends TimelineBase {
this.mastodonService.getBookmarks(this.account)
.then((result: BookmarkResult) => {
this.maxId = result.max_id;
if(!this.maxId){
this.lastCallReachedMax = true;
}
for (const s of result.bookmarked) {
let cwPolicy = this.toolsService.checkContentWarning(s);
const wrapper = new StatusWrapper(cwPolicy.status, this.account, cwPolicy.applyCw, cwPolicy.hide);
@ -73,6 +78,8 @@ export class BookmarksComponent extends TimelineBase {
}
protected getNextStatuses(): Promise<Status[]> {
if(this.lastCallReachedMax) return Promise.resolve([]);
return this.mastodonService.getBookmarks(this.account, this.maxId)
.then((result: BookmarkResult) => {
const statuses = result.bookmarked;

View File

@ -56,6 +56,11 @@ export class FavoritesComponent extends TimelineBase {
this.mastodonService.getFavorites(this.account)
.then((result: FavoriteResult) => {
this.maxId = result.max_id;
if (!this.maxId) {
this.lastCallReachedMax = true;
}
for (const s of result.favorites) {
let cwPolicy = this.toolsService.checkContentWarning(s);
const wrapper = new StatusWrapper(cwPolicy.status, this.account, cwPolicy.applyCw, cwPolicy.hide);
@ -72,12 +77,14 @@ export class FavoritesComponent extends TimelineBase {
}
protected getNextStatuses(): Promise<Status[]> {
return this.mastodonService.getFavorites(this.account, this.maxId)
if (this.lastCallReachedMax) return Promise.resolve([]);
return this.mastodonService.getFavorites(this.account, this.maxId)
.then((result: FavoriteResult) => {
const statuses = result.favorites;
this.maxId = result.max_id;
if(!this.maxId){
if (!this.maxId) {
this.lastCallReachedMax = true;
}
@ -85,7 +92,7 @@ export class FavoritesComponent extends TimelineBase {
});
}
protected scrolledToTop() {}
protected scrolledToTop() { }
protected statusProcessOnGoToTop(){}
protected statusProcessOnGoToTop() { }
}

View File

@ -1,5 +1,5 @@
<div class="card-data" *ngIf="card.type === 'link' || card.type === 'video'">
<a *ngIf="card.type === 'link'" class="card-data__link" href="{{ card.url }}" target="_blank" title="{{ card.title }} &#10;{{ host }}">
<a *ngIf="card.type === 'link'" class="card-data__link" href="{{ card.url }}" target="_blank" rel="noopener noreferrer" title="{{ card.title }} &#10;{{ host }}">
<img *ngIf="card.image" class="card-data__link--image" src="{{ card.image | ensureHttps }}" alt="" />
<div *ngIf="!card.image" class="card-data__link--image">
<fa-icon class="card-data__link--image--logo" [icon]="faFileAlt"></fa-icon>

View File

@ -42,7 +42,7 @@ describe('DatabindedTextComponent', () => {
const url = 'https://test.social/tags/programmers';
const sample = `<p>bla1 <a href="${url}" class="mention hashtag" rel="nofollow noopener" target="_blank">#<span>${hashtag}</span></a> bla2</p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="hashtag-programmers" title="#programmers">#programmers</a>');
expect(component.processedText).toContain(`<a href="${url}" class="hashtag-programmers" title="#programmers" target="_blank" rel="noopener noreferrer">#programmers</a>`);
expect(component.processedText).toContain('bla1');
expect(component.processedText).toContain('bla2');
});
@ -50,7 +50,7 @@ describe('DatabindedTextComponent', () => {
it('should parse hashtag - Pleroma 2.0.2', () => {
const sample = `Blabla <a class="hashtag" data-tag="covid19" href="https://url.com/tag/covid19">#covid19</a> Blibli`;
component.text = sample;
expect(component.processedText).toContain('<a href class="hashtag-covid19" title="#covid19">#covid19</a>');
expect(component.processedText).toContain(`<a href="https://url.com/tag/covid19" class="hashtag-covid19" title="#covid19" target="_blank" rel="noopener noreferrer">#covid19</a>`);
expect(component.processedText).toContain('Blabla');
expect(component.processedText).toContain('Blibli');
});
@ -60,7 +60,7 @@ describe('DatabindedTextComponent', () => {
const url = 'https://mastodon.social/@sengi_app';
const sample = `<p>bla1 <span class="h-card"><a href="${url}" class="u-url mention" rel="nofollow noopener" target="_blank">@<span>${mention}</span></a></span> bla2</p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="account--sengi_app-mastodon-social" title="@sengi_app@mastodon.social">@sengi_app</a>');
expect(component.processedText).toContain(`<a href="${url}" class="account--sengi_app-mastodon-social" title="@sengi_app@mastodon.social" target="_blank" rel="noopener noreferrer">@sengi_app</a>`);
expect(component.processedText).toContain('bla1');
expect(component.processedText).toContain('bla2');
});
@ -69,14 +69,14 @@ describe('DatabindedTextComponent', () => {
const sample = `<p><span class="article-type"><a href="https://domain.name/@username" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@<span class="article-type">username</span></a></span> <br>Yes, indeed.</p>`;
component.text = sample;
expect(component.processedText).toBe('<p><span class="article-type"><a href class="account--username-domain-name" title="@username@domain.name">@username</a> <br>Yes, indeed.</p>');
expect(component.processedText).toBe('<p><span class="article-type"><a href="https://domain.name/@username" class="account--username-domain-name" title="@username@domain.name" target="_blank" rel="noopener noreferrer">@username</a> <br>Yes, indeed.</p>');
});
it('should parse link', () => {
const url = 'mydomain.co/test';
const sample = `<p>bla1 <a href="https://${url}" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">${url}</span><span class="invisible"></span></a> bla2</p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="link-httpsmydomaincotest" title="open link">mydomain.co/test</a>');
expect(component.processedText).toContain(`<a href="https://${url}" class="link-httpsmydomaincotest" title="open link" target="_blank" rel="noopener noreferrer">mydomain.co/test</a>`);
expect(component.processedText).toContain('bla1');
expect(component.processedText).toContain('bla2');
});
@ -85,21 +85,21 @@ describe('DatabindedTextComponent', () => {
const url = 'bbc.com/news/magazine-34901704';
const sample = `<p>The rise of"<br><a href="https:www//${url}" rel="nofollow noopener" target="_blank"><span class="invisible">https://www.</span><span class="">${url}</span><span class="invisible"></span></a></p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="link-httpswwwbbccomnewsmagazine34901704" title="open link">bbc.com/news/magazine-34901704</a></p>');
expect(component.processedText).toContain(`<a href="https:www//${url}" class="link-httpswwwbbccomnewsmagazine34901704" title="open link" target="_blank" rel="noopener noreferrer">bbc.com/news/magazine-34901704</a></p>`);
});
it('should parse link - dual section', () => {
const sample = `<p>Test.<br><a href="https://peertube.fr/videos/watch/69bb6e90-ec0f-49a3-9e28-41792f4a7c5f" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="ellipsis">peertube.fr/videos/watch/69bb6</span><span class="invisible">e90-ec0f-49a3-9e28-41792f4a7c5f</span></a></p>`;
component.text = sample;
expect(component.processedText).toContain('<p>Test.<br><a href class="link-httpspeertubefrvideoswatch69bb6e90ec0f49a39e2841792f4a7c5f" title="open link">peertube.fr/videos/watch/69bb6</a></p>');
expect(component.processedText).toContain('<p>Test.<br><a href="https://peertube.fr/videos/watch/69bb6e90-ec0f-49a3-9e28-41792f4a7c5f" class="link-httpspeertubefrvideoswatch69bb6e90ec0f49a39e2841792f4a7c5f" title="open link" target="_blank" rel="noopener noreferrer">peertube.fr/videos/watch/69bb6</a></p>');
});
it('should parse link with special character', () => {
const sample = `<p>Magnitude: 2.5 Depth: 3.4 km<br>Details: 2018/09/27 06:50:17 34.968N 120.685W<br>Location: 10 km (6 mi) W of Guadalupe, CA<br>Map: <a href="https://www.google.com/maps/place/34°58'4%20N+120°41'6%20W/@34.968,-120.685,10z" rel="noopener" target="_blank" class="status-link" title="https://www.google.com/maps/place/34%C2%B058'4%20N+120%C2%B041'6%20W/@34.968,-120.685,10z"><span class="invisible">https://www.</span><span class="ellipsis">google.com/maps/place/34°58'4%</span><span class="invisible">20N+120°41'6%20W/@34.968,-120.685,10z</span></a><br><a href="https://mastodon.cloud/tags/earthquake" class="mention hashtag status-link" rel="noopener" target="_blank">#<span>EarthQuake</span></a> <a href="https://mastodon.cloud/tags/quake" class="mention hashtag status-link" rel="noopener" target="_blank">#<span>Quake</span></a> <a href="https://mastodon.cloud/tags/california" class="mention hashtag status-link" rel="noopener" target="_blank">#<span>California</span></a></p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="link-httpswwwgooglecommapsplace3458420N12041620W3496812068510z" title="open link">google.com/maps/place/34°58\'4%</a>');
expect(component.processedText).toContain(`<a href="https://www.google.com/maps/place/34°58'4%20N+120°41'6%20W/@34.968,-120.685,10z" class="link-httpswwwgooglecommapsplace3458420N12041620W3496812068510z" title="open link" target="_blank" rel="noopener noreferrer">google.com/maps/place/34°58\'4%</a>`);
});
it('should parse combined hashtag, mention and link', () => {
@ -110,9 +110,9 @@ describe('DatabindedTextComponent', () => {
const linkUrl = 'mydomain.co/test';
const sample = `<p>bla1 <a href="${hashtagUrl}" class="mention hashtag" rel="nofollow noopener" target="_blank">#<span>${hashtag}</span></a> bla2 <span class="h-card"><a href="${mentionUrl}" class="u-url mention" rel="nofollow noopener" target="_blank">@<span>${mention}</span></a></span> bla3 <a href="https://${linkUrl}" rel="nofollow noopener" target="_blank"><span class="invisible">https://</span><span class="">${linkUrl}</span><span class="invisible"></span></a> bla4</p>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="hashtag-programmers" title="#programmers">#programmers</a>');
expect(component.processedText).toContain('<a href class="account--sengi_app-mastodon-social" title="@sengi_app@mastodon.social">@sengi_app</a>');
expect(component.processedText).toContain('<a href class="link-httpsmydomaincotest" title="open link">mydomain.co/test</a>');
expect(component.processedText).toContain(`<a href="${hashtagUrl}" class="hashtag-programmers" title="#programmers" target="_blank" rel="noopener noreferrer">#programmers</a>`);
expect(component.processedText).toContain(`<a href="${mentionUrl}" class="account--sengi_app-mastodon-social" title="@sengi_app@mastodon.social" target="_blank" rel="noopener noreferrer">@sengi_app</a>`);
expect(component.processedText).toContain(`<a href="https://${linkUrl}" class="link-httpsmydomaincotest" title="open link" target="_blank" rel="noopener noreferrer">mydomain.co/test</a>`);
expect(component.processedText).toContain('bla1');
expect(component.processedText).toContain('bla2');
expect(component.processedText).toContain('bla3');
@ -123,7 +123,7 @@ describe('DatabindedTextComponent', () => {
const sample = `bla1 <a href="https://www.lemonde.fr/planete.html?xtor=RSS-3208" rel="nofollow noopener" class="" target="_blank">https://social.bitcast.info/url/819438</a>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="link-httpswwwlemondefrplanetehtmlxtorRSS3208" title="open link">https://social.bitcast.info/url/819438</a>');
expect(component.processedText).toContain('<a href="https://www.lemonde.fr/planete.html?xtor=RSS-3208" class="link-httpswwwlemondefrplanetehtmlxtorRSS3208" title="open link" target="_blank" rel="noopener noreferrer">https://social.bitcast.info/url/819438</a>');
expect(component.processedText).toContain('bla1');
});
@ -131,7 +131,7 @@ describe('DatabindedTextComponent', () => {
const sample = `<div>bla1 <br> @<a href="https://instance.club/user/1" class="h-card mention status-link" rel="noopener" target="_blank" title="https://instance.club/user/1">user</a>&nbsp;</div>`;
component.text = sample;
expect(component.processedText).toContain('<a href class="account--user-instance-club" title="@user@instance.club">@user</a>');
expect(component.processedText).toContain('<a href="https://instance.club/user/1" class="account--user-instance-club" title="@user@instance.club" target="_blank" rel="noopener noreferrer">@user</a>');
expect(component.processedText).toContain('bla1');
});
@ -139,42 +139,56 @@ describe('DatabindedTextComponent', () => {
const sample = `<div><span><a class="mention status-link" href="https://pleroma.site/users/kaniini" rel="noopener" target="_blank" title="kaniini@pleroma.site">@<span>kaniini</span></a></span> <span><a class="mention status-link" href="https://mastodon.social/@Gargron" rel="noopener" target="_blank" title="Gargron@mastodon.social">@<span>Gargron</span></a></span> bla1?</div>`;
component.text = sample;
expect(component.processedText).toContain('<div><span><a href class="account--kaniini-pleroma-site" title="@kaniini@pleroma.site">@kaniini</a> <span><a href class="account--Gargron-mastodon-social" title="@Gargron@mastodon.social">@Gargron</a> bla1?</div>');
expect(component.processedText).toContain('<div><span><a href="https://pleroma.site/users/kaniini" class="account--kaniini-pleroma-site" title="@kaniini@pleroma.site" target="_blank" rel="noopener noreferrer">@kaniini</a> <span><a href="https://mastodon.social/@Gargron" class="account--Gargron-mastodon-social" title="@Gargron@mastodon.social" target="_blank" rel="noopener noreferrer">@Gargron</a> bla1?</div>');
});
it('should parse mention - Friendica in Mastodon', () => {
const sample = `@<span class=""><a href="https://m.s/me" class="u-url mention" rel="nofollow noopener" target="_blank"><span class="mention">me</span></a></span> Blablabla.`;
component.text = sample;
expect(component.processedText).toContain('<span class=""><a href class="account--me-m-s" title="@me@m.s">@me</a></span> Blablabla.');
expect(component.processedText).toContain('<span class=""><a href="https://m.s/me" class="account--me-m-s" title="@me@m.s" target="_blank" rel="noopener noreferrer">@me</a></span> Blablabla.');
});
it('should parse mention - Misskey in Mastodon', () => {
const sample = `<p><a href="https://mastodon.social/users/sengi_app" class="mention" rel="nofollow noopener" target="_blank">@sengi_app@mastodon.social</a><span> Blabla</span></p>`;
component.text = sample;
expect(component.processedText).toContain('<p><a href class="account--sengi_app-mastodon-social-mastodon-social" title="@sengi_app@mastodon.social@mastodon.social">@sengi_app@mastodon.social</a><span> Blabla</span></p>'); //FIXME: dont let domain appear in name
expect(component.processedText).toContain('<p><a href="https://mastodon.social/users/sengi_app" class="account--sengi_app-mastodon-social-mastodon-social" title="@sengi_app@mastodon.social@mastodon.social" target="_blank" rel="noopener noreferrer">@sengi_app@mastodon.social</a><span> Blabla</span></p>'); //FIXME: dont let domain appear in name
});
it('should parse mention - Misskey in Mastodon - 2', () => {
const sample = `<p><span>Since </span><a href="https://mastodon.technology/@test" class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@test@mastodon.technology</a><span> mentioned </span></p>`;
component.text = sample;
expect(component.processedText).toContain('<a href="https://mastodon.technology/@test" class="account--test-mastodon-technology" title="@test@mastodon.technology" target="_blank" rel="noopener noreferrer">@test</a>');
});
it('should parse mention - Zap in Mastodon', () => {
const sample = `test @<span class="h-card"><a class="u-url mention" href="https://mastodon.social/@test" rel="nofollow noopener noreferrer" target="_blank">test</a></span> bla"`;
component.text = sample;
expect(component.processedText).toContain('test <span class="h-card"><a href="https://mastodon.social/@test" class="account--test-mastodon-social" title="@test@mastodon.social" target="_blank" rel="noopener noreferrer">@test</a></span>');
});
it('should parse hastag - Pleroma', () => {
const sample = `<p>Bla <a href="https://ubuntu.social/tags/kubecon" rel="tag">#<span>KubeCon</span></a> Bla</p>`;
component.text = sample;
expect(component.processedText).toContain('<p>Bla <a href class="hashtag-KubeCon" title="#KubeCon">#KubeCon</a> Bla</p>');
expect(component.processedText).toContain('<p>Bla <a href="https://ubuntu.social/tags/kubecon" class="hashtag-KubeCon" title="#KubeCon" target="_blank" rel="noopener noreferrer">#KubeCon</a> Bla</p>');
});
it('should parse link - Pleroma', () => {
const sample = `<p>Bla <a href="https://cloudblogs.microsoft.com/opensource/2019/05/21/service-mesh-interface-smi-release/"><span>https://</span><span>cloudblogs.microsoft.com/opens</span><span>ource/2019/05/21/service-mesh-interface-smi-release/</span></a></p>`;
component.text = sample;
expect(component.processedText).toContain('<p>Bla <a href class="link-httpscloudblogsmicrosoftcomopensource20190521servicemeshinterfacesmirelease" title="open link">cloudblogs.microsoft.com/opens</a></p>');
expect(component.processedText).toContain('<p>Bla <a href="https://cloudblogs.microsoft.com/opensource/2019/05/21/service-mesh-interface-smi-release/" class="link-httpscloudblogsmicrosoftcomopensource20190521servicemeshinterfacesmirelease" title="open link" target="_blank" rel="noopener noreferrer">cloudblogs.microsoft.com/opens</a></p>');
});
it('should parse link 2 - Pleroma', () => {
const sample = `Bla<br /><br /><a href="https://link/">https://link/</a>`;
component.text = sample;
expect(component.processedText).toContain('Bla<br /><br /><a href class="link-httpslink" title="open link">https://link/</a>');
expect(component.processedText).toContain('Bla<br /><br /><a href="https://link/" class="link-httpslink" title="open link" target="_blank" rel="noopener noreferrer">https://link/</a>');
});
it('should sanitize link', () => {

View File

@ -28,7 +28,7 @@ export class DatabindedTextComponent implements OnInit {
@Input('text')
set text(value: string) {
//console.warn(value);
//console.log(value);
let parser = new DOMParser();
var dom = parser.parseFromString(value, 'text/html')
@ -44,6 +44,10 @@ export class DatabindedTextComponent implements OnInit {
value = value.replace('class="mention" rel="nofollow noopener" target="_blank">@', 'class="mention" rel="nofollow noopener" target="_blank">'); //Misskey sanitarization
} while (value.includes('class="mention" rel="nofollow noopener" target="_blank">@'));
do {
value = value.replace('@<span class="h-card">', '<span class="h-card">'); //Zap sanitarization
} while (value.includes('@<span class="h-card">'));
let linksSections = value.split('<a ');
for (let section of linksSections) {
@ -90,9 +94,10 @@ export class DatabindedTextComponent implements OnInit {
private processHashtag(section: string) {
let extractedLinkAndNext = section.split('</a>');
let extractedHashtag = extractedLinkAndNext[0].split('#')[1].replace('<span>', '').replace('</span>', '');
let extractedUrl = extractedLinkAndNext[0].split('href="')[1].split('"')[0];
let classname = this.getClassNameForHastag(extractedHashtag);
this.processedText += ` <a href class="${classname}" title="#${extractedHashtag}">#${extractedHashtag}</a>`;
this.processedText += ` <a href="${extractedUrl}" class="${classname}" title="#${extractedHashtag}" target="_blank" rel="noopener noreferrer">#${extractedHashtag}</a>`;
if (extractedLinkAndNext[1]) this.processedText += extractedLinkAndNext[1];
this.hashtags.push(extractedHashtag);
}
@ -104,9 +109,17 @@ export class DatabindedTextComponent implements OnInit {
if (section.includes('<span class="mention">')) { //Friendica
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('<span class="mention">')[1].split('</span>')[0];
} else if(section.includes('>@<span class="article-type">')){ //Remote status
} else if (section.includes('>@<span class="article-type">')) { //Remote status
extractedAccountAndNext = section.split('</a></span>');
extractedAccountName = extractedAccountAndNext[0].split('@<span class="article-type">')[1].replace('<span>', '').replace('</span>', '');
} else if (section.includes('class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@') && !section.includes('target="_blank">@<')) { //Misskey
//console.warn('misskey');
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('class="u-url mention" rel="nofollow noopener noreferrer" target="_blank">@')[1];
if (extractedAccountName.includes('@'))
extractedAccountName = extractedAccountName.split('@')[0];
} else if (!section.includes('@<span>')) { //GNU social
extractedAccountAndNext = section.split('</a>');
extractedAccountName = extractedAccountAndNext[0].split('>')[1];
@ -121,9 +134,10 @@ export class DatabindedTextComponent implements OnInit {
//let username = extractedAccountLink[extractedAccountLink.length - 1];
let extractedAccount = `@${extractedAccountName}@${domain}`;
let extractedUrl = section.split('href="')[1].split('"')[0];
let classname = this.getClassNameForAccount(extractedAccount);
this.processedText += `<a href class="${classname}" title="${extractedAccount}">@${extractedAccountName}</a>`;
this.processedText += `<a href="${extractedUrl}" class="${classname}" title="${extractedAccount}" target="_blank" rel="noopener noreferrer">@${extractedAccountName}</a>`;
if (extractedAccountAndNext[1])
this.processedText += extractedAccountAndNext[1];
@ -136,7 +150,7 @@ export class DatabindedTextComponent implements OnInit {
}
private processLink(section: string) {
if(!section.includes('</a>')){
if (!section.includes('</a>')) {
this.processedText += section;
return;
}
@ -167,7 +181,9 @@ export class DatabindedTextComponent implements OnInit {
this.links.push(extractedUrl);
let classname = this.getClassNameForLink(extractedUrl);
this.processedText += `<a href class="${classname}" title="open link">${extractedName}</a>`;
let sanitizedLink = this.sanitizeLink(extractedUrl);
this.processedText += `<a href="${sanitizedLink}" class="${classname}" title="open link" target="_blank" rel="noopener noreferrer">${extractedName}</a>`;
if (extractedLinkAndNext.length > 1) this.processedText += extractedLinkAndNext[1];
}
@ -219,20 +235,18 @@ export class DatabindedTextComponent implements OnInit {
this.renderer.listen(el, 'click', (event) => {
event.preventDefault();
event.stopImmediatePropagation();
window.open(sanitizedLink, '_blank');
window.open(sanitizedLink, '_blank', 'noopener');
return false;
});
this.renderer.listen(el, 'mouseup', (event) => {
if (event.which === 2) {
event.preventDefault();
event.stopImmediatePropagation();
window.open(sanitizedLink, '_blank');
return false;
}
});
// this.renderer.listen(el, 'mouseup', (event) => {
// if (event.which === 2) {
// event.preventDefault();
// event.stopImmediatePropagation();
// window.open(sanitizedLink, '_blank', 'noopener');
// return false;
// }
// });
}
}
}

View File

@ -27,4 +27,7 @@
<a href class="poll__refresh" *ngIf="(poll.voted || poll.expired) && !pollLocked" title="refresh poll" (click)="refresh()">refresh</a>
<div class="poll__statistics"><span *ngIf="(poll.voted || poll.expired) && !pollLocked" class="poll__separator">·</span>{{poll.votes_count}} votes<span *ngIf="!poll.expired" class="poll__separator" title="{{ poll.expires_at | timeLeft | async }}">· {{ poll.expires_at | timeLeft | async }}</span></div>
</div>
<div class="poll__error" *ngIf="errorOccuredWhenRetrievingPoll">
Error occured when retrieving the poll
</div>
</div>

View File

@ -27,6 +27,11 @@
margin-top: 10px;
}
&__error {
font-size: 12px;
color: red;
}
&__refresh {
font-size: 0.8em;
color: rgb(101, 121, 160);

View File

@ -22,6 +22,8 @@ export class PollComponent implements OnInit {
choiceType: string;
pollLocked: boolean;
errorOccuredWhenRetrievingPoll: boolean;
private pollSelection: number[] = [];
options: PollOptionWrapper[] = [];
@ -30,7 +32,7 @@ export class PollComponent implements OnInit {
private _poll: Poll;
@Input('poll')
set poll(value: Poll) {
if(!value) return;
if (!value) return;
this._poll = value;
@ -83,6 +85,7 @@ export class PollComponent implements OnInit {
private checkStatus(accounts: AccountInfo[]): void {
this.pollLocked = false;
this.errorOccuredWhenRetrievingPoll = false;
var newSelectedAccount = accounts.find(x => x.isSelected);
const accountChanged = this.selectedAccount.id !== newSelectedAccount.id;
@ -92,7 +95,7 @@ export class PollComponent implements OnInit {
let statusWrapper = new StatusWrapper(this.statusWrapper.status, this.statusWrapper.provider, this.statusWrapper.applyCw, this.statusWrapper.hide);
this.pollPerAccountId[newSelectedAccount.id] = this.toolsService.getStatusUsableByAccount(newSelectedAccount, statusWrapper)
.then((status: Status) => {
if(!status || !(status.poll)) return null;
if (!status || !(status.poll)) return null;
return this.mastodonService.getPoll(newSelectedAccount, status.poll.id);
})
.then((poll: Poll) => {
@ -100,7 +103,9 @@ export class PollComponent implements OnInit {
return poll;
})
.catch(err => {
this.notificationService.notifyHttpError(err, newSelectedAccount);
//this.notificationService.notifyHttpError(err, newSelectedAccount);
this.errorOccuredWhenRetrievingPoll = true;
this.pollPerAccountId[newSelectedAccount.id] = null;
return null;
});
} else if (this.statusWrapper.status.visibility !== 'public' && this.statusWrapper.status.visibility !== 'unlisted' && this.statusWrapper.provider.id !== newSelectedAccount.id) {
@ -115,8 +120,9 @@ export class PollComponent implements OnInit {
this.selectedAccount = newSelectedAccount;
}
vote(): boolean {
if (this.errorOccuredWhenRetrievingPoll) return false;
const selectedAccount = this.selectedAccount;
const pollPromise = this.pollPerAccountId[selectedAccount.id];
@ -140,6 +146,8 @@ export class PollComponent implements OnInit {
}
refresh(): boolean {
if (this.errorOccuredWhenRetrievingPoll) return false;
this.setStatsAtZero();
const selectedAccount = this.selectedAccount;

View File

@ -224,12 +224,17 @@ export class ToolsService {
if (!isProvider) {
statusPromise = statusPromise
.then((foreignStatus: Status) => {
const statusUrl = foreignStatus.url;
const statusUri = foreignStatus.uri;
const statusUrl = foreignStatus.url;
return this.getInstanceInfo(account)
.then(instance => {
let version: 'v1' | 'v2' = 'v1';
if (instance.major >= 3) version = 'v2';
return this.mastodonService.search(account, statusUrl, version, true);
return this.mastodonService.search(account, statusUri, version, true)
.then((results: Results) => {
if(results && results.statuses.length > 0) return results;
return this.mastodonService.search(account, statusUrl, version, true);
});
})
.then((results: Results) => {
return results.statuses[0];