mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 13:09:18 +08:00
Show topics as a list of topics on the User Stream.
This commit is contained in:
parent
3f5ea1ef79
commit
0317cf9608
|
@ -13,5 +13,3 @@
|
|||
|
||||
{{collection contentBinding="filteredContent" classNames="form-horizontal settings" itemViewClass="Discourse.SiteSettingView"}}
|
||||
|
||||
<!-- will remove as soon as I figure out what is going on -->
|
||||
<p><small>Diagnostics: last_message_processed {{diags.last_message_processed}}</small></p>
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
{{#with view.content}}
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
</div>
|
||||
<div class="span11">
|
||||
<label>
|
||||
{{view Ember.Checkbox checkedBinding="enabled" value="true"}}
|
||||
{{unbound description}}
|
||||
</label>
|
||||
</div>
|
||||
{{/with}}
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
</div>
|
||||
<div class="span11">
|
||||
<label>
|
||||
{{view Ember.Checkbox checkedBinding="enabled" value="true"}}
|
||||
{{unbound description}}
|
||||
</label>
|
||||
</div>
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
{{#with view.content}}
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
</div>
|
||||
<div class="span11">
|
||||
{{combobox valueAttribute="value" content=validValues value=value none=allowsNone}}
|
||||
<div class='desc'>{{unbound description}}</div>
|
||||
</div>
|
||||
{{#if dirty}}
|
||||
<div class='span3'>
|
||||
<button class='btn ok' {{action save this}}><i class='icon-ok'></i></button>
|
||||
<button class='btn cancel' {{action cancel this}}><i class='icon-remove'></i></button>
|
||||
</div>
|
||||
<div class="span11">
|
||||
{{combobox valueAttribute="value" content=validValues value=value none=allowsNone}}
|
||||
<div class='desc'>{{unbound description}}</div>
|
||||
</div>
|
||||
{{#if dirty}}
|
||||
<div class='span3'>
|
||||
<button class='btn ok' {{action save this}}><i class='icon-ok'></i></button>
|
||||
<button class='btn cancel' {{action cancel this}}><i class='icon-remove'></i></button>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if overridden}}
|
||||
<button class='btn' href='#' {{action resetDefault this}}>{{i18n admin.site_settings.reset}}</button>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if overridden}}
|
||||
<button class='btn' href='#' {{action resetDefault this}}>{{i18n admin.site_settings.reset}}</button>
|
||||
{{/if}}
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
{{#with view.content}}
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
<div class='span4 offset1'>
|
||||
<h3>{{unbound setting}}</h3>
|
||||
</div>
|
||||
<div class="span11">
|
||||
{{textField value=value classNames="input-xxlarge"}}
|
||||
<div class='desc'>{{unbound description}}</div>
|
||||
</div>
|
||||
{{#if dirty}}
|
||||
<div class='span3'>
|
||||
<button class='btn ok' {{action save this}}><i class='icon-ok'></i></button>
|
||||
<button class='btn cancel' {{action cancel this}}><i class='icon-remove'></i></button>
|
||||
</div>
|
||||
<div class="span11">
|
||||
{{textField value=value classNames="input-xxlarge"}}
|
||||
<div class='desc'>{{unbound description}}</div>
|
||||
</div>
|
||||
{{#if dirty}}
|
||||
<div class='span3'>
|
||||
<button class='btn ok' {{action save this}}><i class='icon-ok'></i></button>
|
||||
<button class='btn cancel' {{action cancel this}}><i class='icon-remove'></i></button>
|
||||
</div>
|
||||
{{else}}
|
||||
{{#if overridden}}
|
||||
<button class='btn' href='#' {{action resetDefault this}}>{{i18n admin.site_settings.reset}}</button>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
{{#if overridden}}
|
||||
<button class='btn' href='#' {{action resetDefault this}}>{{i18n admin.site_settings.reset}}</button>
|
||||
{{/if}}
|
||||
{{/with}}
|
||||
{{/if}}
|
||||
|
|
|
@ -64,6 +64,9 @@ Discourse.URL = Em.Object.createWithMixins({
|
|||
if (this.navigatedToListMore(oldPath, path)) { return; }
|
||||
if (this.navigatedToHome(oldPath, path)) { return; }
|
||||
|
||||
if (path.match(/^\/?users\/[^\/]+$/)) {
|
||||
path += "/activity";
|
||||
}
|
||||
// Be wary of looking up the router. In this case, we have links in our
|
||||
// HTML, say form compiled markdown posts, that need to be routed.
|
||||
var router = this.get('router');
|
||||
|
|
|
@ -87,7 +87,7 @@ Discourse.Utilities = {
|
|||
},
|
||||
|
||||
userUrl: function(username) {
|
||||
return Discourse.getURL("/users/" + username);
|
||||
return Discourse.getURL("/users/" + username.toLowerCase());
|
||||
},
|
||||
|
||||
emailValid: function(email) {
|
||||
|
|
|
@ -24,6 +24,16 @@ Discourse.ListController = Discourse.Controller.extend({
|
|||
});
|
||||
}.property(),
|
||||
|
||||
createTopicText: function() {
|
||||
if (this.get('category.name')) {
|
||||
return I18n.t("topic.create_in", {
|
||||
categoryName: this.get('category.name')
|
||||
});
|
||||
} else {
|
||||
return I18n.t("topic.create");
|
||||
}
|
||||
}.property('category.name'),
|
||||
|
||||
/**
|
||||
Refresh our current topic list
|
||||
|
||||
|
|
|
@ -81,11 +81,11 @@ Discourse.ListTopicsController = Discourse.ObjectController.extend({
|
|||
}.property('allLoaded', 'topics.length'),
|
||||
|
||||
loadMore: function() {
|
||||
this.set('loadingMore', true);
|
||||
var listTopicsController = this;
|
||||
return this.get('model').loadMoreTopics().then(function(hasMoreTopics) {
|
||||
listTopicsController.set('loadingMore', false);
|
||||
return hasMoreTopics;
|
||||
var topicList = this.get('model');
|
||||
return topicList.loadMoreTopics().then(function(moreUrl) {
|
||||
if (!Em.isEmpty(moreUrl)) {
|
||||
Discourse.URL.replaceState(Discourse.getURL("/") + topicList.get('filter') + "/more");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,51 +1,3 @@
|
|||
/**
|
||||
The route for editing a user's "About Me" bio.
|
||||
|
||||
@class PreferencesAboutRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesAboutRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties({ model: model, newBio: model.get('bio_raw') });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
},
|
||||
|
||||
events: {
|
||||
changeAbout: function() {
|
||||
var route = this;
|
||||
var controller = route.controllerFor('preferencesAbout');
|
||||
|
||||
controller.setProperties({ saving: true });
|
||||
return controller.get('model').save().then(function() {
|
||||
controller.set('saving', false);
|
||||
route.transitionTo('user.index');
|
||||
}, function() {
|
||||
// model failed to save
|
||||
controller.set('saving', false);
|
||||
alert(I18n.t('generic_error'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
/**
|
||||
This controller supports actions related to updating your "About Me" bio
|
||||
|
||||
|
|
|
@ -1,21 +1,3 @@
|
|||
/**
|
||||
The common route stuff for a user's preference
|
||||
|
||||
@class PreferencesRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
This controller supports actions related to updating one's preferences
|
||||
|
||||
|
|
|
@ -1,32 +1,3 @@
|
|||
/**
|
||||
The route for editing a user's email
|
||||
|
||||
@class PreferencesEmailRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesEmailRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties({ model: model, newEmail: model.get('email') });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
This controller supports actions related to updating one's email address
|
||||
|
||||
|
|
|
@ -1,32 +1,3 @@
|
|||
/**
|
||||
The route for updating a user's username
|
||||
|
||||
@class PreferencesUsernameRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesUsernameRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
return this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
controller.setProperties({ model: user, newUsername: user.get('username') });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
This controller supports actions related to updating one's username
|
||||
|
||||
|
|
|
@ -1,82 +1,3 @@
|
|||
/**
|
||||
The base route for showing an activity stream.
|
||||
|
||||
@class UserActivityRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserActivityRoute = Discourse.Route.extend({
|
||||
renderTemplate: function() {
|
||||
this.render('user_activity', {into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
this.controllerFor('userActivity').set('model', user);
|
||||
|
||||
var composerController = this.controllerFor('composer');
|
||||
controller.set('model', user);
|
||||
if (Discourse.User.current()) {
|
||||
Discourse.Draft.get('new_private_message').then(function(data) {
|
||||
if (data.draft) {
|
||||
composerController.open({
|
||||
draft: data.draft,
|
||||
draftKey: 'new_private_message',
|
||||
ignoreIfChanged: true,
|
||||
draftSequence: data.draft_sequence
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.UserActivityIndexRoute = Discourse.Route.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user').findStream(this.get('userActionType'));
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('user_stream', {into: 'user_activity', outlet: 'activity'});
|
||||
},
|
||||
|
||||
setupController: function() {
|
||||
this.controllerFor('user_activity').set('userActionType', this.get('userActionType'));
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.UserIndexRoute = Discourse.UserActivityRoute.extend({
|
||||
renderTemplate: function() {
|
||||
this._super();
|
||||
this.render('user_stream', {into: 'user_activity', outlet: 'activity'});
|
||||
},
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user').findStream();
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
this.controllerFor('userActivity').set('model', this.modelFor('user'));
|
||||
this.set('model', model);
|
||||
}
|
||||
});
|
||||
|
||||
// Build all the filter routes
|
||||
Object.keys(Discourse.UserAction.TYPES).forEach(function (userAction) {
|
||||
Discourse["UserActivity" + userAction.classify() + "Route"] = Discourse.UserActivityIndexRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES[userAction]
|
||||
});
|
||||
});
|
||||
|
||||
// // Build the private message routes
|
||||
Discourse.UserPrivateMessagesRoute = Discourse.UserActivityRoute.extend({});
|
||||
Discourse.UserPrivateMessagesIndexRoute = Discourse.UserActivityMessagesReceivedRoute;
|
||||
Discourse.UserPrivateMessagesSentRoute = Discourse.UserActivityMessagesSentRoute;
|
||||
|
||||
/**
|
||||
This controller supports all actions on a user's activity stream
|
||||
|
||||
|
|
39
app/assets/javascripts/discourse/mixins/load_more.js
Normal file
39
app/assets/javascripts/discourse/mixins/load_more.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
This mixin provides the ability to load more items for a view which is
|
||||
scrolled to the bottom.
|
||||
|
||||
@class Discourse.LoadMore
|
||||
@extends Ember.Mixin
|
||||
@uses Discourse.Scrolling
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.LoadMore = Em.Mixin.create(Discourse.Scrolling, {
|
||||
|
||||
scrolled: function(e) {
|
||||
var eyeline = this.get('eyeline');
|
||||
if (eyeline) { eyeline.update(); }
|
||||
},
|
||||
|
||||
loadMore: function() {
|
||||
console.error('loadMore() not defined');
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this._super();
|
||||
var eyeline = new Discourse.Eyeline(this.get('eyelineSelector'));
|
||||
this.set('eyeline', eyeline);
|
||||
|
||||
var paginatedTopicListView = this;
|
||||
eyeline.on('sawBottom', function() {
|
||||
paginatedTopicListView.loadMore();
|
||||
});
|
||||
this.bindScrolling();
|
||||
},
|
||||
|
||||
willRemoveElement: function() {
|
||||
this._super();
|
||||
this.unbindScrolling();
|
||||
}
|
||||
|
||||
});
|
|
@ -23,32 +23,37 @@ Discourse.TopicList = Discourse.Model.extend({
|
|||
},
|
||||
|
||||
loadMoreTopics: function() {
|
||||
var moreUrl, _this = this;
|
||||
|
||||
if (moreUrl = this.get('more_topics_url')) {
|
||||
Discourse.URL.replaceState(Discourse.getURL("/") + (this.get('filter')) + "/more");
|
||||
if (this.get('loadingMore')) { return Ember.RSVP.reject(); }
|
||||
|
||||
var moreUrl = this.get('more_topics_url');
|
||||
if (moreUrl) {
|
||||
|
||||
var topicList = this;
|
||||
this.set('loadingMore', true);
|
||||
|
||||
return Discourse.ajax({url: moreUrl}).then(function (result) {
|
||||
var newTopics, topics, topicsAdded = 0;
|
||||
var topicsAdded = 0;
|
||||
if (result) {
|
||||
// the new topics loaded from the server
|
||||
newTopics = Discourse.TopicList.topicsFrom(result);
|
||||
topics = _this.get("topics");
|
||||
var newTopics = Discourse.TopicList.topicsFrom(result);
|
||||
var topics = topicList.get("topics");
|
||||
|
||||
_this.forEachNew(newTopics, function(t) {
|
||||
topicList.forEachNew(newTopics, function(t) {
|
||||
t.set('highlight', topicsAdded++ === 0);
|
||||
topics.pushObject(t);
|
||||
});
|
||||
|
||||
_this.set('more_topics_url', result.topic_list.more_topics_url);
|
||||
Discourse.set('transient.topicsList', _this);
|
||||
topicList.set('more_topics_url', result.topic_list.more_topics_url);
|
||||
Discourse.set('transient.topicsList', topicList);
|
||||
topicList.set('loadingMore', false);
|
||||
|
||||
return result.topic_list.more_topics_url;
|
||||
}
|
||||
return result.topic_list.more_topics_url;
|
||||
});
|
||||
} else {
|
||||
// Return a promise indicating no more results
|
||||
return Ember.Deferred.promise(function (p) {
|
||||
p.resolve(false);
|
||||
});
|
||||
return Ember.RSVP.reject();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -109,6 +114,9 @@ Discourse.TopicList.reopenClass({
|
|||
categories = this.extractByKey(result.categories, Discourse.Category);
|
||||
users = this.extractByKey(result.users, Discourse.User);
|
||||
topics = Em.A();
|
||||
|
||||
console.log(result.topic_list);
|
||||
|
||||
_.each(result.topic_list.topics,function(ft) {
|
||||
ft.category = categories[ft.category_id];
|
||||
_.each(ft.posters,function(p) {
|
||||
|
|
|
@ -264,6 +264,7 @@ Discourse.User = Discourse.Model.extend({
|
|||
json.user.invited_by = Discourse.User.create(json.user.invited_by);
|
||||
}
|
||||
|
||||
|
||||
user.setProperties(json.user);
|
||||
return user;
|
||||
});
|
||||
|
|
|
@ -99,7 +99,11 @@ Discourse.UserAction = Discourse.Model.extend({
|
|||
}.property('target_username'),
|
||||
|
||||
targetUserUrl: Discourse.computed.url('target_username', '/users/%@'),
|
||||
userUrl: Discourse.computed.url('username', '/users/%@'),
|
||||
usernameLower: function() {
|
||||
return this.get('username').toLowerCase();
|
||||
}.property('username'),
|
||||
|
||||
userUrl: Discourse.computed.url('usernameLower', '/users/%@'),
|
||||
|
||||
postUrl: function() {
|
||||
return Discourse.Utilities.postUrl(this.get('slug'), this.get('topic_id'), this.get('post_number'));
|
||||
|
|
|
@ -18,7 +18,7 @@ Discourse.UserStream = Discourse.Model.extend({
|
|||
|
||||
findItems: function() {
|
||||
var me = this;
|
||||
if(this.get("loading")) { return; }
|
||||
if(this.get("loading")) { return Ember.RSVP.reject(); }
|
||||
this.set("loading",true);
|
||||
|
||||
var url = Discourse.getURL("/user_actions.json?offset=") + this.get('itemsLoaded') + "&username=" + (this.get('user.username_lower'));
|
||||
|
|
|
@ -47,5 +47,3 @@ Discourse.FilteredListRoute = Discourse.Route.extend({
|
|||
Discourse.ListController.filters.forEach(function(filter) {
|
||||
Discourse["List" + (filter.capitalize()) + "Route"] = Discourse.FilteredListRoute.extend({ filter: filter });
|
||||
});
|
||||
|
||||
|
||||
|
|
119
app/assets/javascripts/discourse/routes/preferences_routes.js
Normal file
119
app/assets/javascripts/discourse/routes/preferences_routes.js
Normal file
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
The common route stuff for a user's preference
|
||||
|
||||
@class PreferencesRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
The route for editing a user's "About Me" bio.
|
||||
|
||||
@class PreferencesAboutRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesAboutRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties({ model: model, newBio: model.get('bio_raw') });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
},
|
||||
|
||||
events: {
|
||||
changeAbout: function() {
|
||||
var route = this;
|
||||
var controller = route.controllerFor('preferencesAbout');
|
||||
|
||||
controller.setProperties({ saving: true });
|
||||
return controller.get('model').save().then(function() {
|
||||
controller.set('saving', false);
|
||||
route.transitionTo('user.index');
|
||||
}, function() {
|
||||
// model failed to save
|
||||
controller.set('saving', false);
|
||||
alert(I18n.t('generic_error'));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
The route for editing a user's email
|
||||
|
||||
@class PreferencesEmailRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesEmailRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties({ model: model, newEmail: model.get('email') });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
The route for updating a user's username
|
||||
|
||||
@class PreferencesUsernameRoute
|
||||
@extends Discourse.RestrictedUserRoute
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PreferencesUsernameRoute = Discourse.RestrictedUserRoute.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
return this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
// A bit odd, but if we leave to /preferences we need to re-render that outlet
|
||||
exit: function() {
|
||||
this._super();
|
||||
this.render('preferences', { into: 'user', outlet: 'userOutlet', controller: 'preferences' });
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
controller.setProperties({ model: user, newUsername: user.get('username') });
|
||||
}
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
/**
|
||||
This route shows who a user has invited
|
||||
|
||||
@class UserInvitedRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserInvitedRoute = Discourse.Route.extend({
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
model: function() {
|
||||
return Discourse.InviteList.findInvitedBy(this.modelFor('user'));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
Handles routes related to users.
|
||||
|
||||
@class UserRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserRoute = Discourse.Route.extend({
|
||||
|
||||
model: function(params) {
|
||||
|
||||
// If we're viewing the currently logged in user, return that object
|
||||
// instead.
|
||||
var currentUser = Discourse.User.current();
|
||||
if (currentUser && (params.username.toLowerCase() === currentUser.get('username_lower'))) {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
return Discourse.User.create({username: params.username});
|
||||
},
|
||||
|
||||
afterModel: function() {
|
||||
return this.modelFor('user').findDetails();
|
||||
},
|
||||
|
||||
serialize: function(params) {
|
||||
if (!params) return {};
|
||||
return { username: Em.get(params, 'username').toLowerCase() };
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
controller.set('model', user);
|
||||
|
||||
// Add a search context
|
||||
this.controllerFor('search').set('searchContext', user.get('searchContext'));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this._super();
|
||||
var user = this.modelFor('user');
|
||||
Discourse.MessageBus.subscribe("/users/" + user.get('username_lower'), function(data) {
|
||||
user.loadUserAction(data);
|
||||
});
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this._super();
|
||||
Discourse.MessageBus.unsubscribe("/users/" + this.modelFor('user').get('username_lower'));
|
||||
|
||||
// Remove the search context
|
||||
this.controllerFor('search').set('searchContext', null);
|
||||
}
|
||||
|
||||
|
||||
});
|
192
app/assets/javascripts/discourse/routes/user_routes.js
Normal file
192
app/assets/javascripts/discourse/routes/user_routes.js
Normal file
|
@ -0,0 +1,192 @@
|
|||
/**
|
||||
Handles routes related to users.
|
||||
|
||||
@class UserRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserRoute = Discourse.Route.extend({
|
||||
|
||||
model: function(params) {
|
||||
|
||||
// If we're viewing the currently logged in user, return that object
|
||||
// instead.
|
||||
var currentUser = Discourse.User.current();
|
||||
if (currentUser && (params.username.toLowerCase() === currentUser.get('username_lower'))) {
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
return Discourse.User.create({username: params.username});
|
||||
},
|
||||
|
||||
afterModel: function() {
|
||||
return this.modelFor('user').findDetails();
|
||||
},
|
||||
|
||||
serialize: function(params) {
|
||||
if (!params) return {};
|
||||
return { username: Em.get(params, 'username').toLowerCase() };
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
controller.set('model', user);
|
||||
|
||||
// Add a search context
|
||||
this.controllerFor('search').set('searchContext', user.get('searchContext'));
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this._super();
|
||||
var user = this.modelFor('user');
|
||||
Discourse.MessageBus.subscribe("/users/" + user.get('username_lower'), function(data) {
|
||||
user.loadUserAction(data);
|
||||
});
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this._super();
|
||||
Discourse.MessageBus.unsubscribe("/users/" + this.modelFor('user').get('username_lower'));
|
||||
|
||||
// Remove the search context
|
||||
this.controllerFor('search').set('searchContext', null);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
This route shows who a user has invited
|
||||
|
||||
@class UserInvitedRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserInvitedRoute = Discourse.Route.extend({
|
||||
renderTemplate: function() {
|
||||
this.render({ into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
model: function() {
|
||||
return Discourse.InviteList.findInvitedBy(this.modelFor('user'));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
The base route for showing a user's activity
|
||||
|
||||
@class UserActivityRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserActivityRoute = Discourse.Route.extend({
|
||||
renderTemplate: function() {
|
||||
this.render('user_activity', {into: 'user', outlet: 'userOutlet' });
|
||||
},
|
||||
|
||||
model: function() {
|
||||
return this.modelFor('user');
|
||||
},
|
||||
|
||||
setupController: function(controller, user) {
|
||||
this.controllerFor('userActivity').set('model', user);
|
||||
|
||||
var composerController = this.controllerFor('composer');
|
||||
controller.set('model', user);
|
||||
if (Discourse.User.current()) {
|
||||
Discourse.Draft.get('new_private_message').then(function(data) {
|
||||
if (data.draft) {
|
||||
composerController.open({
|
||||
draft: data.draft,
|
||||
draftKey: 'new_private_message',
|
||||
ignoreIfChanged: true,
|
||||
draftSequence: data.draft_sequence
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
Discourse.UserPrivateMessagesRoute = Discourse.UserActivityRoute.extend({});
|
||||
|
||||
/**
|
||||
If we request /user/eviltrout without a sub route.
|
||||
|
||||
@class UserIndexRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserIndexRoute = Discourse.UserActivityRoute.extend({
|
||||
redirect: function() {
|
||||
this.transitionTo('userActivity', this.modelFor('user'));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
The base route for showing an activity stream.
|
||||
|
||||
@class UserActivityStreamRoute
|
||||
@extends Discourse.Route
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserActivityStreamRoute = Discourse.Route.extend({
|
||||
model: function() {
|
||||
return this.modelFor('user').findStream(this.get('userActionType'));
|
||||
},
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('user_stream', {into: 'user_activity', outlet: 'activity'});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model', model);
|
||||
this.controllerFor('user_activity').set('userActionType', this.get('userActionType'));
|
||||
}
|
||||
});
|
||||
|
||||
// Build all activity stream routes
|
||||
['bookmarks', 'edits', 'likes_given', 'likes_received', 'replies', 'posts', 'index'].forEach(function (userAction) {
|
||||
Discourse["UserActivity" + userAction.classify() + "Route"] = Discourse.UserActivityStreamRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES[userAction]
|
||||
});
|
||||
});
|
||||
|
||||
Discourse.UserPrivateMessagesIndexRoute = Discourse.UserActivityStreamRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES.messages_received
|
||||
});
|
||||
Discourse.UserPrivateMessagesSentRoute = Discourse.UserActivityStreamRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES.messages_sent
|
||||
});
|
||||
|
||||
//Discourse.UserTopicsListView = Em.View.extend({ templateName: 'user/topics_list' });
|
||||
Discourse.UserTopicListRoute = Discourse.Route.extend({
|
||||
|
||||
renderTemplate: function() {
|
||||
this.render('paginated_topic_list', {into: 'user_activity', outlet: 'activity'});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
this.controllerFor('user_activity').set('userActionType', this.get('userActionType'));
|
||||
controller.set('model', model);
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.UserActivityTopicsRoute = Discourse.UserTopicListRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES.topics,
|
||||
|
||||
model: function() {
|
||||
return Discourse.TopicList.find('topics/created-by/' + this.modelFor('user').get('username_lower'));
|
||||
}
|
||||
});
|
||||
|
||||
Discourse.UserActivityFavoritesRoute = Discourse.UserTopicListRoute.extend({
|
||||
userActionType: Discourse.UserAction.TYPES.favorites,
|
||||
|
||||
model: function() {
|
||||
return Discourse.TopicList.find('favorited');
|
||||
}
|
||||
});
|
|
@ -1,20 +1,18 @@
|
|||
{{#with view.content}}
|
||||
<div class='row'>
|
||||
<div class='topic-meta-data span2'>
|
||||
<div class='contents'>
|
||||
<div>
|
||||
<a href='/users/{{unbound username}}'>{{avatar this imageSize="small"}}</a>
|
||||
</div>
|
||||
<h5 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}'>{{breakUp username}}</a></h5>
|
||||
<div class='row'>
|
||||
<div class='topic-meta-data span2'>
|
||||
<div class='contents'>
|
||||
<div>
|
||||
<a href='/users/{{unbound username}}'>{{avatar this imageSize="small"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class='span11 topic-body'>
|
||||
<div class="topic-meta-data-inside">
|
||||
<div class='post-info post-date'>{{unboundAgeWithTooltip created_at}}</div>
|
||||
{{#if view.previousPost}}<a href='{{unbound url}}' class="post-info arrow" title="{{i18n topic.jump_reply_up}}"><i class='icon icon-arrow-up'></i></a>{{/if}}
|
||||
</div>
|
||||
{{{unbound cooked}}}
|
||||
{{#unless view.previousPost}}<a href='{{unbound url}}' class="arrow" title="{{i18n topic.jump_reply_down}}"><i class='icon icon-arrow-down'></i></a>{{/unless}}
|
||||
<h5 {{bindAttr class="staff new_user"}}><a href='{{unbound usernameUrl}}'>{{breakUp username}}</a></h5>
|
||||
</div>
|
||||
</div>
|
||||
{{/with}}
|
||||
<div class='span11 topic-body'>
|
||||
<div class="topic-meta-data-inside">
|
||||
<div class='post-info post-date'>{{unboundAgeWithTooltip created_at}}</div>
|
||||
{{#if view.previousPost}}<a href='{{unbound url}}' class="post-info arrow" title="{{i18n topic.jump_reply_up}}"><i class='icon icon-arrow-up'></i></a>{{/if}}
|
||||
</div>
|
||||
{{{unbound cooked}}}
|
||||
{{#unless view.previousPost}}<a href='{{unbound url}}' class="arrow" title="{{i18n topic.jump_reply_down}}"><i class='icon icon-arrow-down'></i></a>{{/unless}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<div id='list-controls'>
|
||||
<div class="container">
|
||||
<ul class="nav nav-pills" id='category-filter'>
|
||||
{{each availableNavItems itemViewClass="Discourse.NavItemView"}}
|
||||
</ul>
|
||||
|
||||
{{#if canCreateTopic}}
|
||||
<button class='btn btn-default' {{action createTopic}}><i class='icon icon-plus'></i>{{createTopicText}}</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if canEditCategory}}
|
||||
<button class='btn btn-default' {{action editCategory category}}>{{i18n category.edit_long}}</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if canCreateCategory}}
|
||||
<button class='btn btn-default' {{action createCategory}}><i class='icon icon-plus'></i>{{i18n category.create}}</button>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="full-width">
|
||||
<div id='list-area'>
|
||||
{{#if loading}}
|
||||
<div class='contents loading'>
|
||||
<table id='topic-list'>
|
||||
<tr>
|
||||
<td colspan='8'>
|
||||
<div class='spinner'>{{i18n loading}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{outlet listView}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
<table id="topic-list">
|
||||
<tr>
|
||||
<th>
|
||||
{{i18n topic.title}}
|
||||
</th>
|
||||
<th>{{i18n category_title}}</th>
|
||||
<th class='num posts'>{{i18n posts}}</th>
|
||||
<th class='num likes'>{{i18n likes}}</th>
|
||||
<th class='num views'>{{i18n views}}</th>
|
||||
<th class='num activity' colspan='2'>{{i18n activity}}</th>
|
||||
</tr>
|
||||
|
||||
{{#group}}
|
||||
{{#collection contentBinding="view.topics" tagName="tbody" itemTagName="tr"}}
|
||||
<td class='main-link'>
|
||||
<a class='title' href="{{unbound lastReadUrl}}">{{{unbound fancy_title}}}</a>
|
||||
{{#if unread}}
|
||||
<a href="{{unbound lastReadUrl}}" class='badge unread badge-notification' title='{{i18n topic.unread_posts count="unread"}}'>{{unbound unread}}</a>
|
||||
{{/if}}
|
||||
{{#if new_posts}}
|
||||
<a href="{{unbound lastReadUrl}}" class='badge new-posts badge-notification' title='{{i18n topic.new_posts count="new_posts"}}'>{{unbound new_posts}}</a>
|
||||
{{/if}}
|
||||
{{#if unseen}}
|
||||
<a href="{{lastReadUrl}}" class='badge new-posts badge-notification' title='{{i18n topic.new}}'><i class='icon icon-asterisk'></i></a>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class='category'>
|
||||
{{categoryLink category}}
|
||||
</td>
|
||||
<td class='num posts'><a href="{{lastReadUrl}}" class='badge-posts'>{{number posts_count numberKey="posts_long"}}</a></td>
|
||||
|
||||
<td class='num likes'>
|
||||
{{#if like_count}}
|
||||
<a href='{{url}}{{#if has_best_of}}?filter=best_of{{/if}}'>{{like_count}} <i class='icon-heart'></i></a>
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
<td {{bindAttr class=":num :views viewsHeat"}}>{{number views numberKey="views_long"}}</td>
|
||||
{{#if bumped}}
|
||||
<td class='num activity'>
|
||||
<a href="{{url}}" {{{bindAttr class=":age ageCold"}}} title='{{i18n first_post}}: {{{unboundDate created_at}}}' >{{unboundAge created_at}}</a>
|
||||
</td>
|
||||
<td class='num activity last'>
|
||||
<a href="{{lastPostUrl}}" class='age' title='{{i18n last_post}}: {{{unboundDate bumped_at}}}'>{{unboundAge bumped_at}}</a>
|
||||
</td>
|
||||
{{else}}
|
||||
<td class='num activity'>
|
||||
<a href="{{url}}" class='age' title='{{i18n first_post}}: {{{unboundDate created_at}}}'>{{unboundAge created_at}}</a>
|
||||
</td>
|
||||
<td class="activity"></td>
|
||||
{{/if}}
|
||||
{{/collection}}
|
||||
{{/group}}
|
||||
|
||||
</table>
|
|
@ -5,7 +5,7 @@
|
|||
</ul>
|
||||
|
||||
{{#if canCreateTopic}}
|
||||
<button class='btn btn-default' {{action createTopic}}><i class='icon icon-plus'></i>{{view.createTopicText}}</button>
|
||||
<button class='btn btn-default' {{action createTopic}}><i class='icon icon-plus'></i>{{createTopicText}}</button>
|
||||
{{/if}}
|
||||
|
||||
{{#if canEditCategory}}
|
||||
|
@ -21,7 +21,6 @@
|
|||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
||||
<div class="full-width">
|
||||
<div id='list-area'>
|
||||
{{#if loading}}
|
||||
|
@ -39,7 +38,6 @@
|
|||
{{outlet listView}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
{{#with view.content}}
|
||||
<a href='{{unbound url}}'>
|
||||
<span class='badge-category' style="background-color: #{{unbound color}}; color: #{{unbound text_color}};">{{unbound title}}</span>
|
||||
</a>
|
||||
{{/with}}
|
||||
|
||||
<a href='{{unbound url}}'>
|
||||
<span class='badge-category' style="background-color: #{{unbound color}}; color: #{{unbound text_color}};">{{unbound title}}</span>
|
||||
</a>
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
{{#with view.content}}
|
||||
<a href='{{unbound url}}'>
|
||||
{{unbound title}}
|
||||
</a>
|
||||
{{/with}}
|
||||
|
||||
<a href='{{unbound url}}'>
|
||||
{{unbound title}}
|
||||
</a>
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
{{#with view.content}}
|
||||
<a href='{{unbound url}}'>
|
||||
{{avatar this usernamePath="title" imageSize="small"}}
|
||||
{{unbound title}}
|
||||
</a>
|
||||
{{/with}}
|
||||
|
||||
<a href='{{unbound url}}'>
|
||||
{{avatar this usernamePath="title" imageSize="small"}}
|
||||
{{unbound title}}
|
||||
</a>
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
{{#with view.content}}
|
||||
{{#group}}
|
||||
<td class='main-link'>
|
||||
<a class='title' href="{{unbound lastReadUrl}}">{{{unbound fancy_title}}}</a>
|
||||
{{#if unread}}
|
||||
<a href="{{unbound lastReadUrl}}" class='badge unread badge-notification' title='{{i18n topic.unread_posts count="unread"}}'>{{unbound unread}}</a>
|
||||
{{/if}}
|
||||
{{#if new_posts}}
|
||||
<a href="{{unbound lastReadUrl}}" class='badge new-posts badge-notification' title='{{i18n topic.new_posts count="new_posts"}}'>{{unbound new_posts}}</a>
|
||||
{{/if}}
|
||||
{{#if unseen}}
|
||||
<a href="{{lastReadUrl}}" class='badge new-posts badge-notification' title='{{i18n topic.new}}'><i class='icon icon-asterisk'></i></a>
|
||||
{{/if}}
|
||||
</td>
|
||||
<td class='category'>
|
||||
{{categoryLink category}}
|
||||
</td>
|
||||
<td class='num posts'><a href="{{lastReadUrl}}" class='badge-posts'>{{number posts_count numberKey="posts_long"}}</a></td>
|
||||
|
||||
<td class='num likes'>
|
||||
{{#if like_count}}
|
||||
<a href='{{url}}{{#if has_best_of}}?filter=best_of{{/if}}'>{{like_count}} <i class='icon-heart'></i></a>
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
<td {{bindAttr class=":num :views viewsHeat"}}>{{number views numberKey="views_long"}}</td>
|
||||
|
||||
{{#if bumped}}
|
||||
<td class='num activity'>
|
||||
<a href="{{url}}" {{{bindAttr class=":age ageCold"}}} title='{{i18n first_post}}: {{{unboundDate created_at}}}' >{{unboundAge created_at}}</a>
|
||||
</td>
|
||||
<td class='num activity last'>
|
||||
<a href="{{lastPostUrl}}" class='age' title='{{i18n last_post}}: {{{unboundDate bumped_at}}}'>{{unboundAge bumped_at}}</a>
|
||||
</td>
|
||||
{{else}}
|
||||
<td class='num activity'>
|
||||
<a href="{{url}}" class='age' title='{{i18n first_post}}: {{{unboundDate created_at}}}'>{{unboundAge created_at}}</a>
|
||||
</td>
|
||||
<td class="activity"></td>
|
||||
{{/if}}
|
||||
|
||||
{{/group}}
|
||||
{{/with}}
|
|
@ -74,24 +74,9 @@
|
|||
|
||||
{{#if details.suggested_topics.length}}
|
||||
<div id='suggested-topics'>
|
||||
|
||||
<h3>{{i18n suggested_topics.title}}</h3>
|
||||
|
||||
<div class='topics'>
|
||||
<table id="topic-list">
|
||||
<tr>
|
||||
<th>
|
||||
{{i18n topic.title}}
|
||||
</th>
|
||||
<th>{{i18n category_title}}</th>
|
||||
<th class='num posts'>{{i18n posts}}</th>
|
||||
<th class='num likes'>{{i18n likes}}</th>
|
||||
<th class='num views'>{{i18n views}}</th>
|
||||
<th class='num activity' colspan='2'>{{i18n activity}}</th>
|
||||
</tr>
|
||||
|
||||
{{each details.suggested_topics itemTagName="tr" itemViewClass="Discourse.SuggestedTopicView"}}
|
||||
</table>
|
||||
{{basicTopicList topics=details.suggested_topics}}
|
||||
</div>
|
||||
<br/>
|
||||
<h3>{{{view.browseMoreMessage}}}</h3>
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
|
||||
|
||||
{{outlet activity}}
|
||||
<div id='user-activity'>
|
||||
{{outlet activity}}
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,24 +1,21 @@
|
|||
{{#with view.content}}
|
||||
<div {{bindAttr class=":item hidden deleted moderator_action"}}>
|
||||
<div class='clearfix info'>
|
||||
<a href="{{unbound userUrl}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
|
||||
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
||||
<span class="title">
|
||||
<a href="{{unbound postUrl}}">{{unbound title}}</a>
|
||||
</span>
|
||||
<span class="type">{{unbound descriptionHtml}}</span>
|
||||
</div>
|
||||
<p class='excerpt'>
|
||||
{{{unbound excerpt}}}
|
||||
</p>
|
||||
{{#each children}}
|
||||
<div class='child-actions'>
|
||||
<i class="icon {{unbound icon}}"></i>
|
||||
{{#each items}}
|
||||
<a href="{{unbound userUrl}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="tiny" extraClasses="actor" ignoreTitle="true"}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
<div {{bindAttr class=":item hidden deleted moderator_action"}}>
|
||||
<div class='clearfix info'>
|
||||
<a href="{{unbound userUrl}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="large" extraClasses="actor" ignoreTitle="true"}}</div></a>
|
||||
<span class='time'>{{date path="created_at" leaveAgo="true"}}</span>
|
||||
<span class="title">
|
||||
<a href="{{unbound postUrl}}">{{unbound title}}</a>
|
||||
</span>
|
||||
<span class="type">{{unbound descriptionHtml}}</span>
|
||||
</div>
|
||||
{{/with}}
|
||||
|
||||
<p class='excerpt'>
|
||||
{{{unbound excerpt}}}
|
||||
</p>
|
||||
{{#each children}}
|
||||
<div class='child-actions'>
|
||||
<i class="icon {{unbound icon}}"></i>
|
||||
{{#each items}}
|
||||
<a href="{{unbound userUrl}}" class='avatar-link'><div class='avatar-wrapper'>{{avatar this imageSize="tiny" extraClasses="actor" ignoreTitle="true"}}</div></a>
|
||||
{{/each}}
|
||||
</div>
|
||||
{{/each}}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
no
|
|
@ -10,6 +10,11 @@ Discourse.EmbeddedPostView = Discourse.View.extend({
|
|||
templateName: 'embedded_post',
|
||||
classNames: ['reply'],
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('context', this.get('content'));
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
Discourse.ScreenTrack.instance().track(this.get('elementId'), this.get('post.post_number'));
|
||||
},
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
This view is used for rendering a basic list of topics.
|
||||
|
||||
@class BasicTopicListView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.BasicTopicListView = Discourse.View.extend({
|
||||
templateName: 'list/basic_topic_list'
|
||||
});
|
||||
Discourse.View.registerHelper('basicTopicList', Discourse.BasicTopicListView);
|
|
@ -4,34 +4,24 @@
|
|||
@class ListTopicsView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@uses Discourse.Scrolling
|
||||
@uses Discourse.LoadMore
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
|
||||
Discourse.ListTopicsView = Discourse.View.extend(Discourse.LoadMore, {
|
||||
templateName: 'list/topics',
|
||||
categoryBinding: 'controller.controllers.list.category',
|
||||
canCreateTopicBinding: 'controller.controllers.list.canCreateTopic',
|
||||
listBinding: 'controller.model',
|
||||
loadedMore: false,
|
||||
currentTopicId: null,
|
||||
eyelineSelector: '.topic-list-item',
|
||||
|
||||
topicTrackingState: function() {
|
||||
return Discourse.TopicTrackingState.current();
|
||||
}.property(),
|
||||
|
||||
willDestroyElement: function() {
|
||||
this.unbindScrolling();
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.bindScrolling();
|
||||
var eyeline = new Discourse.Eyeline('.topic-list-item');
|
||||
|
||||
var listTopicsView = this;
|
||||
eyeline.on('sawBottom', function() {
|
||||
listTopicsView.loadMore();
|
||||
});
|
||||
|
||||
this._super();
|
||||
var scrollPos = Discourse.get('transient.topicListScrollPos');
|
||||
if (scrollPos) {
|
||||
Em.run.schedule('afterRender', function() {
|
||||
|
@ -42,15 +32,10 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
|
|||
$('html, body').scrollTop(0);
|
||||
});
|
||||
}
|
||||
this.set('eyeline', eyeline);
|
||||
},
|
||||
|
||||
showTable: function() {
|
||||
var topics = this.get('list.topics');
|
||||
if(topics) {
|
||||
return this.get('list.topics').length > 0 || this.get('topicTrackingState.hasIncoming');
|
||||
}
|
||||
}.property('list.topics.@each','topicTrackingState.hasIncoming'),
|
||||
hasTopics: Em.computed.gt('list.topics.length', 0),
|
||||
showTable: Em.computed.or('hasTopics', 'topicTrackingState.hasIncoming'),
|
||||
|
||||
updateTitle: function(){
|
||||
Discourse.notifyTitle(this.get('topicTrackingState.incomingCount'));
|
||||
|
@ -76,9 +61,8 @@ Discourse.ListTopicsView = Discourse.View.extend(Discourse.Scrolling, {
|
|||
|
||||
// When the topic list is scrolled
|
||||
scrolled: function(e) {
|
||||
this._super();
|
||||
this.saveScrollPos();
|
||||
var eyeline = this.get('eyeline');
|
||||
if (eyeline) { eyeline.update(); }
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/**
|
||||
This view handles the rendering of a list
|
||||
|
||||
@class ListView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.ListView = Discourse.View.extend({
|
||||
templateName: 'list/list',
|
||||
composeViewBinding: Ember.Binding.oneWay('Discourse.composeView'),
|
||||
|
||||
// The window has been scrolled
|
||||
scrolled: function(e) {
|
||||
var currentView;
|
||||
currentView = this.get('container.currentView');
|
||||
return currentView ? typeof currentView.scrolled === "function" ? currentView.scrolled(e) : void 0 : void 0;
|
||||
},
|
||||
|
||||
createTopicText: function() {
|
||||
if (this.get('controller.category.name')) {
|
||||
return I18n.t("topic.create_in", {
|
||||
categoryName: this.get('controller.category.name')
|
||||
});
|
||||
} else {
|
||||
return I18n.t("topic.create");
|
||||
}
|
||||
}.property('controller.category.name')
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
This view is used for rendering a basic list of topics.
|
||||
|
||||
@class PaginatedTopicListView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@uses Discourse.LoadMore
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.PaginatedTopicListView = Discourse.BasicTopicListView.extend(Discourse.LoadMore, {
|
||||
topics: Em.computed.alias('controller.model.topics'),
|
||||
classNames: ['paginated-topics-list'],
|
||||
eyelineSelector: '.paginated-topics-list #topic-list tr',
|
||||
|
||||
loadMore: function() {
|
||||
this.get('controller.model').loadMoreTopics();
|
||||
}
|
||||
|
||||
});
|
||||
|
|
@ -12,9 +12,7 @@ Discourse.TopicListItemView = Discourse.View.extend({
|
|||
classNameBindings: ['content.archived', ':topic-list-item', 'content.hasExcerpt:has-excerpt'],
|
||||
attributeBindings: ['data-topic-id'],
|
||||
|
||||
'data-topic-id': function() {
|
||||
return this.get('content.id');
|
||||
}.property('content.id'),
|
||||
'data-topic-id': Em.computed.alias('content.id'),
|
||||
|
||||
init: function() {
|
||||
this._super();
|
||||
|
|
|
@ -10,17 +10,14 @@ Discourse.SearchResultsTypeView = Ember.CollectionView.extend({
|
|||
tagName: 'ul',
|
||||
itemViewClass: Ember.View.extend({
|
||||
tagName: 'li',
|
||||
classNameBindings: ['selectedClass'],
|
||||
classNameBindings: ['selected'],
|
||||
templateName: Discourse.computed.fmt('parentView.type', "search/%@_result"),
|
||||
selected: Discourse.computed.propertyEqual('content.index', 'controller.selectedIndex'),
|
||||
|
||||
templateName: function() {
|
||||
return "search/" + (this.get('parentView.type')) + "_result";
|
||||
}.property('parentView.type'),
|
||||
|
||||
// Is this row currently selected by the keyboard?
|
||||
selectedClass: function() {
|
||||
if (this.get('content.index') === this.get('controller.selectedIndex')) return 'selected';
|
||||
return null;
|
||||
}.property('controller.selectedIndex')
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('context', this.get('content'));
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
/**
|
||||
This view is used for rendering a suggested topic
|
||||
|
||||
@class SuggestedTopicView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.SuggestedTopicView = Discourse.View.extend({
|
||||
templateName: 'suggested_topic'
|
||||
});
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ Discourse.ActivityFilterView = Discourse.View.extend({
|
|||
url: function() {
|
||||
var section = this.get('content.isPM') ? "/private-messages" : "/activity";
|
||||
return "/users/" + this.get('user.username_lower') + section + this.get('typeKey');
|
||||
}.property('typeKey'),
|
||||
}.property('typeKey', 'user.username_lower'),
|
||||
|
||||
description: function() {
|
||||
return this.get('content.description') || I18n.t("user.filters.all");
|
||||
|
|
|
@ -4,45 +4,32 @@
|
|||
@class UserStreamView
|
||||
@extends Discourse.View
|
||||
@namespace Discourse
|
||||
@uses Discourse.Scrolling
|
||||
@uses Discourse.LoadMore
|
||||
@module Discourse
|
||||
**/
|
||||
Discourse.UserStreamView = Ember.CollectionView.extend(Discourse.Scrolling, {
|
||||
Discourse.UserStreamView = Ember.CollectionView.extend(Discourse.LoadMore, {
|
||||
loading: false,
|
||||
elementId: 'user-stream',
|
||||
content: Em.computed.alias('controller.model.content'),
|
||||
itemViewClass: Ember.View.extend({ templateName: 'user/stream_item' }),
|
||||
eyelineSelector: '#user-activity .user-stream .item',
|
||||
classNames: ['user-stream'],
|
||||
|
||||
scrolled: function(e) {
|
||||
var eyeline = this.get('eyeline');
|
||||
if (eyeline) { eyeline.update(); }
|
||||
},
|
||||
itemViewClass: Ember.View.extend({
|
||||
templateName: 'user/stream_item',
|
||||
init: function() {
|
||||
this._super();
|
||||
this.set('context', this.get('content'));
|
||||
}
|
||||
}),
|
||||
|
||||
loadMore: function() {
|
||||
var userStreamView = this;
|
||||
if (userStreamView.get('loading')) { return; }
|
||||
|
||||
var stream = this.get('stream');
|
||||
var stream = this.get('controller.model');
|
||||
stream.findItems().then(function() {
|
||||
userStreamView.set('loading', false);
|
||||
userStreamView.get('eyeline').flushRest();
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement: function() {
|
||||
this.unbindScrolling();
|
||||
},
|
||||
|
||||
didInsertElement: function() {
|
||||
this.bindScrolling();
|
||||
|
||||
var eyeline = new Discourse.Eyeline('#user-stream .item');
|
||||
this.set('eyeline', eyeline);
|
||||
|
||||
var userStreamView = this;
|
||||
eyeline.on('sawBottom', function() {
|
||||
userStreamView.loadMore();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
//= require ./pagedown_custom.js
|
||||
|
||||
// Stuff we need to load first
|
||||
//= require ./discourse/mixins/scrolling
|
||||
//= require_tree ./discourse/mixins
|
||||
//= require ./discourse/components/computed
|
||||
//= require ./discourse/views/view
|
||||
|
|
|
@ -282,6 +282,11 @@
|
|||
height: 20px;
|
||||
}
|
||||
|
||||
#topic-list {
|
||||
th {
|
||||
background-color: $topic-list-th-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
#suggested-topics {
|
||||
margin: 40px 0 40px 20px;
|
||||
|
@ -294,9 +299,6 @@
|
|||
color: darken($darkish_gray, 20%);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
th {
|
||||
background-color: $topic-list-th-background-color;
|
||||
}
|
||||
}
|
||||
|
||||
#topic-footer-buttons {
|
||||
|
|
|
@ -237,62 +237,64 @@
|
|||
}
|
||||
}
|
||||
|
||||
#user-stream {
|
||||
#user-activity {
|
||||
width: 840px;
|
||||
float: left;
|
||||
margin-bottom: 50px;
|
||||
|
||||
.excerpt {
|
||||
margin: 5px 0px;
|
||||
font-size: 13px;
|
||||
word-wrap: break-word;
|
||||
color: lighten($black, 30%);
|
||||
}
|
||||
.item.moderator-action {
|
||||
background-color: #eef0ff;
|
||||
}
|
||||
.item.deleted {
|
||||
opacity: 0.8;
|
||||
background-color: #ffcece;
|
||||
}
|
||||
.item.hidden {
|
||||
display: block;
|
||||
opacity: 0.4;
|
||||
}
|
||||
.item {
|
||||
padding: 10px 8px;
|
||||
background-color: white;
|
||||
border: 1px solid #b9b9b9;
|
||||
margin-bottom: 10px;
|
||||
@include border-radius-all(4px);
|
||||
@include box-shadow((0 1px 2px rgba($black, 0.07), inset 0 -4px 4px -4px rgba($black, 0.14)));
|
||||
}
|
||||
.type {
|
||||
color: lighten($black, 40%);
|
||||
}
|
||||
span.name {
|
||||
color: lighten($black, 40%);
|
||||
}
|
||||
.time {
|
||||
display: block;
|
||||
float: right;
|
||||
color: silver;
|
||||
margin-right: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.avatar-link {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.title {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
.user-stream {
|
||||
.excerpt {
|
||||
margin: 5px 0px;
|
||||
font-size: 13px;
|
||||
word-wrap: break-word;
|
||||
color: lighten($black, 30%);
|
||||
}
|
||||
.item.moderator-action {
|
||||
background-color: #eef0ff;
|
||||
}
|
||||
.item.deleted {
|
||||
opacity: 0.8;
|
||||
background-color: #ffcece;
|
||||
}
|
||||
.item.hidden {
|
||||
display: block;
|
||||
opacity: 0.4;
|
||||
}
|
||||
.item {
|
||||
padding: 10px 8px;
|
||||
background-color: white;
|
||||
border: 1px solid #b9b9b9;
|
||||
margin-bottom: 10px;
|
||||
@include border-radius-all(4px);
|
||||
@include box-shadow((0 1px 2px rgba($black, 0.07), inset 0 -4px 4px -4px rgba($black, 0.14)));
|
||||
}
|
||||
.type {
|
||||
color: lighten($black, 40%);
|
||||
}
|
||||
span.name {
|
||||
color: lighten($black, 40%);
|
||||
}
|
||||
.time {
|
||||
display: block;
|
||||
float: right;
|
||||
color: silver;
|
||||
margin-right: 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.avatar-link {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
}
|
||||
.title {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// styling of bottom section
|
||||
#user-stream .child-actions {
|
||||
.user-stream .child-actions {
|
||||
margin-top: 8px;
|
||||
.avatar-link {
|
||||
float: none;
|
||||
|
@ -312,12 +314,12 @@
|
|||
}
|
||||
|
||||
@include medium-width {
|
||||
#user-stream {
|
||||
#user-activity {
|
||||
width: 725px;
|
||||
}
|
||||
}
|
||||
@include small-width {
|
||||
#user-stream {
|
||||
#user-activity {
|
||||
width: 680px;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
class ListController < ApplicationController
|
||||
|
||||
before_filter :ensure_logged_in, except: [:latest, :hot, :category, :category_feed, :latest_feed, :hot_feed]
|
||||
before_filter :ensure_logged_in, except: [:latest, :hot, :category, :category_feed, :latest_feed, :hot_feed, :topics_by]
|
||||
before_filter :set_category, only: [:category, :category_feed]
|
||||
skip_before_filter :check_xhr
|
||||
|
||||
|
@ -28,6 +28,14 @@ class ListController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
def topics_by
|
||||
list_opts = build_topic_list_options
|
||||
list = TopicQuery.new(current_user, list_opts).list_topics_by(fetch_user_from_params)
|
||||
list.more_topics_url = url_for(topics_by_path(list_opts.merge(format: 'json', page: next_page)))
|
||||
|
||||
respond(list)
|
||||
end
|
||||
|
||||
def category
|
||||
query = TopicQuery.new(current_user, page: params[:page])
|
||||
|
||||
|
|
|
@ -201,6 +201,7 @@ Discourse::Application.routes.draw do
|
|||
post 't' => 'topics#create'
|
||||
post 'topics/timings'
|
||||
get 'topics/similar_to'
|
||||
get 'topics/created-by/:username' => 'list#topics_by', as: 'topics_by', constraints: {username: USERNAME_ROUTE_FORMAT}
|
||||
|
||||
# Legacy route for old avatars
|
||||
get 'threads/:topic_id/:post_number/avatar' => 'topics#avatar', constraints: {topic_id: /\d+/, post_number: /\d+/}
|
||||
|
|
|
@ -8,7 +8,7 @@ task 'integration:create_fixtures' => :environment do
|
|||
topic: ["/t/280.json"],
|
||||
user: ["/users/eviltrout.json",
|
||||
"/user_actions.json?offset=0&username=eviltrout",
|
||||
"/user_actions.json?offset=0&username=eviltrout&filter=4",
|
||||
"/topics/created-by/eviltrout.json",
|
||||
"/user_actions.json?offset=0&username=eviltrout&filter=5",
|
||||
"/user_actions.json?offset=0&username=eviltrout&filter=6,7,9",
|
||||
"/user_actions.json?offset=0&username=eviltrout&filter=1",
|
||||
|
|
|
@ -129,6 +129,14 @@ class TopicQuery
|
|||
create_list(:posted) {|l| l.where('tu.user_id IS NOT NULL') }
|
||||
end
|
||||
|
||||
def list_topics_by(user)
|
||||
Rails.logger.info ">>> #{user.id}"
|
||||
create_list(:user_topics) do |topics|
|
||||
topics.where(user_id: user.id)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def list_uncategorized
|
||||
create_list(:uncategorized, unordered: true) do |list|
|
||||
list = list.where(category_id: nil)
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -6,7 +6,7 @@ test("Activity Streams", function() {
|
|||
var streamTest = function(url) {
|
||||
visit(url).then(function() {
|
||||
ok(exists(".user-heading"), "The heading is rendered");
|
||||
ok(exists("#user-stream"), "The stream is rendered");
|
||||
ok(exists("#user-activity"), "The activity is rendered");
|
||||
});
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user