Skip to content
Next
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Switch to GitLab Next
Sign in / Register
Toggle navigation
Minds Frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Locked Files
Issues
822
Issues
822
List
Boards
Labels
Service Desk
Milestones
Merge Requests
62
Merge Requests
62
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Minds
Minds Frontend
Compare Revisions
00c7130bf0a16cf022611a8a5554ccf2e1c144e8...65b5bbb330f2ec6290b8878a89d517456b65d7f8
Source
65b5bbb330f2ec6290b8878a89d517456b65d7f8
Select Git revision
...
Target
00c7130bf0a16cf022611a8a5554ccf2e1c144e8
Select Git revision
Compare
Commits (2)
Admin firehose
· ea29be81
Brian Hatchet
authored
8 hours ago
ea29be81
Merge branch 'admin_firehose' into 'master'
· 65b5bbb3
Mark Harding
authored
8 hours ago
Admin firehose See merge request
!295
65b5bbb3
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
448 additions
and
6 deletions
+448
-6
nsfw-selector.component.html
...mon/components/nsfw-selector/nsfw-selector.component.html
+1
-1
nsfw-selector.component.ts
...ommon/components/nsfw-selector/nsfw-selector.component.ts
+2
-2
admin.html
src/app/controllers/admin/admin.html
+7
-0
firehose.component.html
src/app/controllers/admin/firehose/firehose.component.html
+32
-0
firehose.component.scss
src/app/controllers/admin/firehose/firehose.component.scss
+42
-0
firehose.component.spec.ts
...app/controllers/admin/firehose/firehose.component.spec.ts
+158
-0
firehose.component.ts
src/app/controllers/admin/firehose/firehose.component.ts
+185
-0
declarations.ts
src/app/declarations.ts
+2
-0
creator.component.ts
src/app/modules/report/creator/creator.component.ts
+16
-1
tslint.json
tslint.json
+3
-2
No files found.
src/app/common/components/nsfw-selector/nsfw-selector.component.html
View file @
65b5bbb3
<m-dropdown
class=
"m-nsfwSelector__dropdown"
#
dropdown
>
<m-dropdown
class=
"m-nsfwSelector__dropdown"
[
expanded
]="
expanded
"
#
dropdown
>
<label
class=
"m-nsfwSelector__label m-posterActionBar__IconAndLabel"
[
class
.
selected
]="
hasSelections
()"
>
...
...
This diff is collapsed.
Click to expand it.
src/app/common/components/nsfw-selector/nsfw-selector.component.ts
View file @
65b5bbb3
...
...
@@ -2,7 +2,7 @@ import {
Component
,
EventEmitter
,
Input
,
Output
,
Output
,
}
from
'@angular/core'
;
import
{
NSFWSelectorCreatorService
,
...
...
@@ -28,6 +28,7 @@ export class NSFWSelectorComponent {
@
Input
(
'service'
)
serviceRef
:
string
=
'consumer'
;
@
Input
(
'consumer'
)
consumer
:
false
;
@
Input
(
'expanded'
)
expanded
:
false
;
@
Output
(
'selected'
)
onSelected
:
EventEmitter
<
any
>
=
new
EventEmitter
();
constructor
(
...
...
@@ -77,5 +78,4 @@ export class NSFWSelectorComponent {
return
true
;
}
}
}
This diff is collapsed.
Click to expand it.
src/app/controllers/admin/admin.html
View file @
65b5bbb3
...
...
@@ -19,6 +19,12 @@
>
<span
i18n=
"@@M__ADMIN_NAV__BOOSTS"
>
Boosts
</span>
</a>
<a
class=
"m-topbar--navigation--item"
routerLink=
"/admin/firehose"
routerLinkActive=
"m-topbar--navigation--item-active"
>
<span
i18n=
"@@M__ADMIN_NAV__FIREHOSE"
>
Firehose
</span>
</a>
<a
class=
"m-topbar--navigation--item"
routerLink=
"/admin/pages"
routerLinkActive=
"m-topbar--navigation--item-active"
...
...
@@ -84,6 +90,7 @@
</div>
<m-admin--interactions
*
ngIf=
"filter == 'interactions'"
></m-admin--interactions>
<minds-admin-boosts
*
ngIf=
"filter == 'boosts'"
></minds-admin-boosts>
<minds-admin-firehose
*
ngIf=
"filter == 'firehose'"
></minds-admin-firehose>
<minds-admin-pages
*
ngIf=
"filter == 'pages'"
></minds-admin-pages>
<minds-admin-reports
*
ngIf=
"filter == 'reports' || filter == 'appeals'"
></minds-admin-reports>
<minds-admin-monetization
*
ngIf=
"filter == 'monetization'"
></minds-admin-monetization>
...
...
This diff is collapsed.
Click to expand it.
src/app/controllers/admin/firehose/firehose.component.html
0 → 100644
View file @
65b5bbb3
<div
class=
"m-firehose m-page mdl-grid"
>
<div
class=
"m-firehose__moderatorAction m-firehose__moderatorAction--leftButton mdl-color--white m-border mdl-cell mdl-cell--1-col"
(
click
)="
reject
()"
tabindex=
0
>
<i
class=
"material-icons"
>
highlight_off
</i>
</div>
<div
class=
"mdl-cell mdl-cell--10-col"
>
<div
class=
"m-firehose__sort-container m-border"
>
<m-sort-selector
[
algorithm
]="
algorithm
"
[
period
]="
period
"
[
customType
]="
customType
"
(
onChange
)="
setSort
($
event
.
algorithm
,
$
event
.
period
,
$
event
.
customType
)"
></m-sort-selector>
</div>
<minds-activity
*
ngIf=
"entity"
[
object
]="
entity
"
class=
"mdl-card m-border item"
></minds-activity>
</div>
<div
class=
"m-firehose__moderatorAction m-firehose__moderatorAction--rightButton mdl-color--white m-border mdl-cell mdl-cell--1-col"
(
click
)="
accept
()"
tabindex=
1
>
<i
class=
"material-icons"
>
check_circle
</i>
</div>
<ng-container
*
ngIf=
"inProgress"
>
<div
class=
"m-firehose__spinner mdl-cell mdl-cell--12-col"
>
<div
class=
"mdl-spinner mdl-spinner--single-color mdl-js-spinner is-active"
></div>
</div>
</ng-container>
</div>
This diff is collapsed.
Click to expand it.
src/app/controllers/admin/firehose/firehose.component.scss
0 → 100644
View file @
65b5bbb3
.m-firehose
{
max-width
:
1280px
;
.m-firehose__moderatorAction
{
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
padding
:
20px
;
margin-bottom
:
25px
;
max-width
:
270px
;
&
.m-firehose__moderatorAction--leftButton
{
justify-content
:
right
;
}
&
.m-firehose__moderatorAction--rightButton
{
justify-content
:
left
;
}
}
.m-firehose__sort-container
{
display
:
flex
;
flex-direction
:
row
;
align-items
:
center
;
justify-content
:
space-between
;
padding
:
12px
;
margin-bottom
:
16px
;
@include
m-theme
(){
background-color
:
themed
(
$m-white
);
}
m-sort-selector
{
flex-grow
:
1
;
}
}
.m-firehose__spinner
{
text-align
:
center
;
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
src/app/controllers/admin/firehose/firehose.component.spec.ts
0 → 100644
View file @
65b5bbb3
import
{
async
,
ComponentFixture
,
fakeAsync
,
TestBed
,
tick
}
from
'@angular/core/testing'
;
import
{
Component
,
Input
,
Output
}
from
'@angular/core'
;
import
{
sessionMock
}
from
'../../../../tests/session-mock.spec'
;;
import
{
Client
}
from
'../../../services/api/client'
;
import
{
By
}
from
'@angular/platform-browser'
;
import
{
clientMock
}
from
'../../../../tests/client-mock.spec'
;
import
{
AdminFirehoseComponent
}
from
'./firehose.component'
;
import
{
Session
}
from
'../../../services/session'
;
import
{
RouterTestingModule
}
from
'@angular/router/testing'
;
import
{
NewsfeedHashtagSelectorService
}
from
'../../../modules/newsfeed/services/newsfeed-hashtag-selector.service'
;
import
{
newsfeedHashtagSelectorServiceMock
}
from
'../../../../tests/newsfeed-hashtag-selector-service-mock.spec'
;
import
{
overlayModalServiceMock
}
from
'../../../../tests/overlay-modal-service-mock.spec'
;
import
{
OverlayModalService
}
from
'../../../services/ux/overlay-modal'
;
import
{
EventEmitter
}
from
'@angular/core'
;
@
Component
({
selector
:
'minds-activity'
,
template
:
''
})
class
MindsActivityMockComponent
{
@
Input
()
object
:
any
;
}
@
Component
({
selector
:
'm-sort-selector'
,
template
:
''
})
class
MindsSortSelectorMockComponent
{
@
Input
()
algorithm
:
string
;
@
Input
()
period
:
string
;
@
Input
()
customType
:
string
;
@
Output
()
onChange
:
EventEmitter
<
any
>
=
new
EventEmitter
<
any
>
();
}
describe
(
'AdminFirehose'
,
()
=>
{
let
comp
:
AdminFirehoseComponent
;
let
fixture
:
ComponentFixture
<
AdminFirehoseComponent
>
;
function
getMockActivities
()
{
return
[
{
guid
:
1
},
{
guid
:
2
},
{
guid
:
3
}
];
}
beforeEach
(
async
(()
=>
{
TestBed
.
configureTestingModule
({
declarations
:
[
MindsActivityMockComponent
,
AdminFirehoseComponent
,
MindsSortSelectorMockComponent
,
],
imports
:
[
RouterTestingModule
],
providers
:
[
{
provide
:
Session
,
useValue
:
sessionMock
},
{
provide
:
Client
,
useValue
:
clientMock
},
{
provide
:
NewsfeedHashtagSelectorService
,
useValue
:
newsfeedHashtagSelectorServiceMock
},
{
provide
:
OverlayModalService
,
useValue
:
overlayModalServiceMock
},
]
})
.
compileComponents
();
}));
beforeEach
((
done
)
=>
{
fixture
=
TestBed
.
createComponent
(
AdminFirehoseComponent
);
comp
=
fixture
.
componentInstance
;
comp
.
entities
=
getMockActivities
();
fixture
.
detectChanges
();
clientMock
.
response
=
{};
clientMock
.
response
[
`api/v2/admin/firehose/latest/activities?hashtags=&period=12h&all=`
]
=
{
'status'
:
'success'
,
'entities'
:
getMockActivities
()
}
if
(
fixture
.
isStable
())
{
done
();
}
else
{
fixture
.
whenStable
().
then
(()
=>
{
done
();
});
}
});
it
(
'should have a loading screen'
,
()
=>
{
comp
.
inProgress
=
true
;
fixture
.
detectChanges
();
expect
(
fixture
.
debugElement
.
query
(
By
.
css
(
'.m-firehose__spinner'
)))
.
not
.
toBeNull
();
});
it
(
'should hide a loading screen'
,
()
=>
{
comp
.
inProgress
=
false
;
fixture
.
detectChanges
();
expect
(
fixture
.
debugElement
.
query
(
By
.
css
(
'.m-firehose__spinner'
)))
.
toBeNull
();
});
it
(
'should initialize entities'
,
()
=>
{
comp
.
entities
=
getMockActivities
();
expect
(
comp
.
entities
.
length
).
toEqual
(
3
);
expect
(
comp
.
entities
).
not
.
toBeNull
();
comp
.
initializeEntity
();
expect
(
comp
.
entity
).
toEqual
(
getMockActivities
()[
0
]);
expect
(
comp
.
entities
.
length
).
toEqual
(
2
);
});
it
(
'should save an accept activity'
,
fakeAsync
(()
=>
{
clientMock
.
response
[
'api/v2/admin/firehose/1'
]
=
{
'status'
:
'success'
};
comp
.
save
(
getMockActivities
()[
0
].
guid
);
fixture
.
detectChanges
();
tick
();
expect
(
clientMock
.
post
).
toHaveBeenCalled
();
expect
(
clientMock
.
post
.
calls
.
mostRecent
().
args
[
0
]).
toContain
(
'api/v2/admin/firehose/1'
);
}));
it
(
'should save a reported activity'
,
fakeAsync
(()
=>
{
clientMock
.
response
[
'api/v2/admin/firehose/1'
]
=
{
'status'
:
'success'
};
comp
.
save
(
getMockActivities
()[
0
].
guid
,
1
,
1
);
fixture
.
detectChanges
();
tick
();
expect
(
clientMock
.
post
).
toHaveBeenCalled
();
expect
(
clientMock
.
post
.
calls
.
mostRecent
().
args
[
0
]).
toContain
(
'api/v2/admin/firehose/1'
);
expect
(
clientMock
.
post
.
calls
.
mostRecent
().
args
[
1
]).
toEqual
({
'reason'
:
1
,
'subreason'
:
1
});
}));
it
(
'should accept an activity'
,
fakeAsync
(()
=>
{
spyOn
(
comp
,
'save'
);
spyOn
(
comp
,
'initializeEntity'
);
comp
.
accept
();
expect
(
clientMock
.
post
).
toHaveBeenCalled
();
expect
(
clientMock
.
post
.
calls
.
mostRecent
().
args
[
0
]).
toContain
(
'api/v2/admin/firehose/1'
);
expect
(
comp
.
save
).
toHaveBeenCalled
();
expect
(
comp
.
initializeEntity
).
toHaveBeenCalled
();
}));
it
(
'should swipe left'
,
fakeAsync
(()
=>
{
spyOn
(
comp
,
'reject'
).
and
.
callThrough
();
comp
.
onKeyPress
({
key
:
'ArrowLeft'
});
expect
(
comp
.
reject
).
toHaveBeenCalled
();
}));
it
(
'should swipe right'
,
fakeAsync
(()
=>
{
spyOn
(
comp
,
'accept'
).
and
.
callThrough
();
comp
.
onKeyPress
({
key
:
'ArrowRight'
});
expect
(
comp
.
accept
).
toHaveBeenCalled
();
}));
});
This diff is collapsed.
Click to expand it.
src/app/controllers/admin/firehose/firehose.component.ts
0 → 100644
View file @
65b5bbb3
import
{
Component
,
HostListener
,
OnInit
,
OnDestroy
}
from
'@angular/core'
;
import
{
Client
}
from
'../../../services/api'
;
import
{
Session
}
from
'../../../services/session'
;
import
{
OverlayModalService
}
from
'../../../services/ux/overlay-modal'
;
import
{
ActivatedRoute
,
Router
}
from
'@angular/router'
;
import
{
Subscription
}
from
'rxjs'
;
import
{
NewsfeedHashtagSelectorService
}
from
'../../../modules/newsfeed/services/newsfeed-hashtag-selector.service'
;
import
{
ReportCreatorComponent
}
from
'../../../modules/report/creator/creator.component'
;
@
Component
({
moduleId
:
module
.
id
,
selector
:
'minds-admin-firehose'
,
templateUrl
:
'firehose.component.html'
,
})
export
class
AdminFirehoseComponent
implements
OnInit
,
OnDestroy
{
entities
:
Array
<
any
>
=
[];
entity
:
any
=
null
;
inProgress
=
true
;
algorithm
=
'latest'
;
period
=
'12h'
;
customType
=
'activities'
;
hashtag
:
string
|
null
=
null
;
all
=
false
;
paramsSubscription
:
Subscription
;
timeout
:
any
=
null
;
constructor
(
private
session
:
Session
,
public
client
:
Client
,
public
router
:
Router
,
public
route
:
ActivatedRoute
,
protected
newsfeedHashtagSelectorService
:
NewsfeedHashtagSelectorService
,
private
overlayModal
:
OverlayModalService
,
)
{
this
.
paramsSubscription
=
this
.
route
.
params
.
subscribe
(
params
=>
{
this
.
algorithm
=
params
[
'algorithm'
]
||
'latest'
;
this
.
period
=
params
[
'period'
]
||
'12h'
;
this
.
customType
=
params
[
'type'
]
||
'activities'
;
if
(
typeof
params
[
'hashtag'
]
!==
'undefined'
)
{
this
.
hashtag
=
params
[
'hashtag'
]
||
null
;
this
.
all
=
false
;
}
else
if
(
typeof
params
[
'all'
]
!==
'undefined'
)
{
this
.
hashtag
=
null
;
this
.
all
=
true
;
}
else
if
(
params
[
'query'
])
{
this
.
all
=
true
;
this
.
updateSortRoute
();
}
else
{
this
.
hashtag
=
null
;
this
.
all
=
false
;
}
if
(
this
.
algorithm
!==
'top'
&&
(
this
.
customType
===
'channels'
||
this
.
customType
===
'groups'
)
)
{
this
.
algorithm
=
'top'
;
this
.
updateSortRoute
();
}
this
.
load
();
});
}
ngOnInit
()
{}
ngOnDestroy
()
{
if
(
this
.
paramsSubscription
)
{
this
.
paramsSubscription
.
unsubscribe
();
}
this
.
timeout
=
null
;
}
public
async
load
()
{
this
.
inProgress
=
true
;
const
hashtags
=
this
.
hashtag
?
encodeURIComponent
(
this
.
hashtag
)
:
''
;
const
period
=
this
.
period
||
''
;
const
all
=
this
.
all
?
'1'
:
''
;
try
{
const
url
=
`api/v2/admin/firehose/
${
this
.
algorithm
}
/
${
this
.
customType
}
?hashtags=
${
hashtags
}
&period=
${
period
}
&all=
${
all
}
`
;
console
.
log
(
url
);
const
response
:
any
=
await
this
.
client
.
get
(
url
);
this
.
entities
=
response
.
entities
;
if
(
this
.
entities
.
length
>
0
)
{
this
.
initializeEntity
();
this
.
timeout
=
setTimeout
(()
=>
this
.
load
(),
3600000
);
}
}
catch
(
exception
)
{
console
.
error
(
exception
);
}
this
.
inProgress
=
false
;
}
public
initializeEntity
()
{
this
.
entity
=
null
;
if
(
this
.
entities
.
length
>
0
)
{
this
.
entity
=
this
.
entities
.
shift
();
}
else
{
this
.
load
();
}
}
public
save
(
guid
:
number
,
reason
:
number
=
null
,
subreason
:
number
=
null
)
{
const
data
=
{
'reason'
:
reason
,
'subreason'
:
subreason
};
return
this
.
client
.
post
(
'api/v2/admin/firehose/'
+
guid
,
data
);
}
public
reject
()
{
const
options
=
{
onReported
:
(
guid
,
reason
,
subreason
)
=>
{
this
.
save
(
guid
,
reason
,
subreason
);
this
.
initializeEntity
();
}
};
this
.
overlayModal
.
create
(
ReportCreatorComponent
,
this
.
entity
,
options
).
present
();
}
public
accept
()
{
this
.
save
(
this
.
entity
.
guid
);
this
.
initializeEntity
();
}
@
HostListener
(
'document:keydown'
,
[
'$event'
])
public
onKeyPress
(
e
)
{
if
(
this
.
entity
)
{
switch
(
e
.
key
)
{
case
'ArrowLeft'
:
this
.
reject
();
break
;
case
'ArrowRight'
:
this
.
accept
();
break
;
}
}
}
public
setSort
(
algorithm
:
string
,
period
:
string
|
null
,
customType
:
string
|
null
)
{
this
.
algorithm
=
algorithm
;
this
.
period
=
period
;
this
.
customType
=
customType
;
this
.
updateSortRoute
();
}
updateSortRoute
()
{
const
route
:
any
[]
=
[
'admin/firehose'
,
this
.
algorithm
];
const
params
:
any
=
{};
params
.
algorithm
=
this
.
algorithm
;
if
(
this
.
period
)
{
params
.
period
=
this
.
period
;
}
if
(
this
.
customType
)
{
params
.
type
=
this
.
customType
;
}
if
(
this
.
hashtag
)
{
params
.
hashtag
=
this
.
hashtag
;
}
else
if
(
this
.
all
)
{
params
.
all
=
1
;
}
route
.
push
(
params
);
this
.
router
.
navigate
(
route
);
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
src/app/declarations.ts
View file @
65b5bbb3
import
{
AdminReportsDownload
}
from
'./controllers/admin/reports-download/reports-download'
;
import
{
AdminBoosts
}
from
'./controllers/admin/boosts/boosts'
;
import
{
AdminFirehoseComponent
}
from
'./controllers/admin/firehose/firehose.component'
;
import
{
AdminPages
}
from
'./controllers/admin/pages/pages'
;
import
{
AdminReports
}
from
'./controllers/admin/reports/reports'
;
import
{
AdminMonetization
}
from
'./controllers/admin/monetization/monetization'
;
...
...
@@ -22,6 +23,7 @@ export const MINDS_DECLARATIONS: any[] = [
AdminInteractions
,
RejectionReasonModalComponent
,
AdminBoosts
,
AdminFirehoseComponent
,
AdminPages
,
AdminReports
,
AdminMonetization
,
...
...
This diff is collapsed.
Click to expand it.
src/app/modules/report/creator/creator.component.ts
View file @
65b5bbb3
...
...
@@ -3,6 +3,7 @@ import { OverlayModalService } from '../../../services/ux/overlay-modal';
import
{
Client
}
from
'../../../services/api'
;
import
{
Session
}
from
'../../../services/session'
;
import
{
REASONS
}
from
'../../../services/list-options'
;
import
{
EventEmitter
}
from
"@angular/core"
;
@
Component
({
moduleId
:
module
.
id
,
...
...
@@ -36,6 +37,12 @@ export class ReportCreatorComponent implements AfterViewInit {
this
.
guid
=
object
?
object
.
guid
:
null
;
}
_opts
:
any
;
set
opts
(
opts
:
any
)
{
this
.
_opts
=
opts
;
}
constructor
(
public
session
:
Session
,
private
_changeDetectorRef
:
ChangeDetectorRef
,
...
...
@@ -112,7 +119,7 @@ export class ReportCreatorComponent implements AfterViewInit {
try
{
let
response
:
any
=
await
this
.
client
.
post
(
`api/v2/moderation/report`
,
{
entity_guid
:
this
.
guid
,
entity_guid
:
this
.
guid
,
reason_code
:
this
.
subject
.
value
,
note
:
this
.
note
,
sub_reason_code
:
this
.
subReason
.
value
,
...
...
@@ -120,6 +127,14 @@ export class ReportCreatorComponent implements AfterViewInit {
this
.
inProgress
=
false
;
this
.
success
=
true
;
if
(
this
.
session
.
isAdmin
())
{
this
.
close
();
}
if
(
this
.
_opts
&&
this
.
_opts
.
onReported
)
{
this
.
_opts
.
onReported
(
this
.
guid
,
this
.
subject
.
value
,
this
.
subReason
.
value
);
}
}
catch
(
e
)
{
this
.
inProgress
=
false
;
//this.overlayModal.dismiss();\
...
...
This diff is collapsed.
Click to expand it.
tslint.json
View file @
65b5bbb3
...
...
@@ -23,7 +23,8 @@
"import-spacing"
:
true
,
"indent"
:
[
true
,
"spaces"
"spaces"
,
2
],
"interface-over-type-literal"
:
true
,
"label-position"
:
true
,
...
...
@@ -125,7 +126,7 @@
"component-selector"
:
[
true
,
"element"
,
"
app
"
,
"
minds
"
,
"kebab-case"
],
"no-output-on-prefix"
:
true
,
...
...
This diff is collapsed.
Click to expand it.