1
0
mirror of https://github.com/flarum/core.git synced 2025-08-13 20:04:24 +02:00

Hello world!

This commit is contained in:
Toby Zerner
2014-12-20 16:56:46 +10:30
commit 74db323f83
279 changed files with 11954 additions and 0 deletions

View File

View File

@@ -0,0 +1,12 @@
import Ember from 'ember';
var DiscussionResult = Ember.ObjectProxy.extend({
relevantPosts: Em.A(),
startPost: null,
lastPost: null
});
export default DiscussionResult;

View File

@@ -0,0 +1,7 @@
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Model.extend({
readTime: DS.attr('date'),
readNumber: DS.attr('number')
});

View File

@@ -0,0 +1,81 @@
import Ember from 'ember';
import DS from 'ember-data';
var Discussion = DS.Model.extend({
title: DS.attr('string'),
slug: function() {
return this.get('title').toLowerCase().replace(/[^a-z0-9]/gi, '-').replace(/-+/g, '-');
}.property('title'),
canReply: DS.attr('boolean'),
canEdit: DS.attr('boolean'),
canDelete: DS.attr('boolean'),
startTime: DS.attr('date'),
startUser: DS.belongsTo('user'),
startPost: DS.belongsTo('post'),
lastTime: DS.attr('date'),
lastUser: DS.belongsTo('user'),
lastPost: DS.belongsTo('post'),
lastPostNumber: DS.attr('number'),
relevantPosts: DS.hasMany('post'),
postsCount: DS.attr('number'),
repliesCount: function() {
return Math.max(0, this.get('postsCount') - 1);
}.property('postsCount'),
posts: DS.attr('string'),
postIds: function() {
return this.get('posts').split(',');
}.property('posts'),
readNumber: DS.attr('number'),
unreadCount: function() {
return this.get('lastPostNumber') - this.get('readNumber');
}.property('lastPostNumber', 'readNumber'),
//--------------------------------
// Prototype generated properties
// category: function() {
// var categories = [null, 'Announcements', 'General', 'Support', 'Feedback', 'Core', 'Plugins', 'Themes'];
// return categories[Math.floor(Math.random() * categories.length)];
// }.property(),
category: DS.attr('string'),
_recent: function() {
var cutoff = new Date('September 19, 2014');
return this.get('lastTime') > cutoff;
}.property('lastTime'),
unread: function() {
return Math.round(Math.random() * (this.get('_recent') ? 0.8 : 0) * this.get('postsCount'));
}.property(),
// sticky: function() {
// return Math.random() > (this.get('_recent') ? 0.95 : 0.99);
// }.property(),
sticky: DS.attr('boolean'),
excerpt: function() {
// return 'I want to get your thoughts on this one TV Addicts: what new show have you been getting into this year, and why?';
// return 'Here\'s the near-final game list, in no particular order. The list may be subject to amendments, as we\'re still chasing up copies of some games.';
// return 'Nominating for the Annual General Meeting is easy. Read this to find out how.'
return 'There are many apps made with Ninetech in the Mac App Store. If you\'d like, take a moment to share your Nintech-made apps in this thread.';
}.property(),
locked: function() {
return Math.random() > 0.95;
}.property(),
following: function() {
return Math.random() > 0.95;
}.property()
});
export default Discussion;

10
ember/app/models/group.js Normal file
View File

@@ -0,0 +1,10 @@
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Model.extend({
name: DS.attr('string'),
users: DS.hasMany('group'),
});

View File

@@ -0,0 +1,20 @@
import Ember from 'ember';
var PostResult = Ember.ObjectProxy.extend({
relevantContent: ''
});
PostResult.reopenClass({
create: function(post) {
if (!post) return null;
var result = this._super();
result.set('content', post);
result.set('relevantContent', post.get('content'));
return result;
}
});
export default PostResult;

View File

@@ -0,0 +1,201 @@
import Ember from 'ember';
// The post stream is an object which represents the posts in a discussion as
// they are displayed on the discussion page, from top to bottom. ...
export default Ember.ArrayProxy.extend(Ember.Evented, {
// An array of all of the post IDs, in chronological order, in the discussion.
ids: Em.A(),
content: Em.A(),
store: null,
discussion: null,
postLoadCount: 20,
_init: function() {
this.clear();
}.on('init'),
setup: function(ids) {
this.set('ids', ids);
this.clear();
},
count: function() {
return this.get('ids.length');
}.property('ids'),
firstLoaded: function() {
var first = this.objectAt(0);
return first && ! first.gap;
}.property('content.@each'),
lastLoaded: function() {
var last = this.objectAt(this.get('length') - 1);
return last && ! last.gap;
}.property('content.@each'),
// Clear the contents of the post stream, resetting it to one big gap.
clear: function() {
var stream = this.get('content');
stream.enumerableContentWillChange();
stream.clear().pushObject(Em.Object.create({
gap: true,
indexStart: 0,
indexEnd: this.get('count') - 1,
loading: true
}));
stream.enumerableContentDidChange();
},
loadRange: function(start, end, backwards) {
var limit = this.get('postLoadCount');
end = end || start + limit;
// Find the appropriate gap objects in the post stream. When we find
// one, we will turn on its loading flag.
this.get('content').forEach(function(item) {
if (item.gap && (
(item.indexStart >= start && item.indexStart <= end)
|| (item.indexEnd >= start && item.indexEnd <= end)
)) {
item.set('loading', true);
item.set('direction', backwards ? 'up' : 'down');
}
});
// Get a list of post numbers that we'll want to retrieve. If there are
// more post IDs than the number of posts we want to load, then take a
// slice of the array in the appropriate direction.
var ids = this.get('ids').slice(start, end + 1);
ids = backwards ? ids.slice(-limit) : ids.slice(0, limit);
return this.loadPosts(ids);
},
loadPosts: function(ids) {
if (! ids.length) {
return Ember.RSVP.resolve();
}
var stream = this;
return this.store.find('post', {ids: ids}).then(function(posts) {
stream.addPosts(posts);
});
},
loadNearNumber: function(number) {
// Find the item in the post stream which is nearest to this number. If
// it turns out the be the actual post we're trying to load, then we can
// return a resolved promise (i.e. we don't need to make an API
// request.) Or, if it's a gap, we'll switch on its loading flag.
var item = this.findNearestToNumber(number);
if (item) {
if (item.get('post.number') == number) {
return Ember.RSVP.resolve([item.get('post')]);
} else if (item.gap) {
item.set('direction', 'down').set('loading', true);
}
}
var stream = this;
return this.store.find('post', {
discussions: this.get('discussion.id'),
near: number
}).then(function(posts) {
stream.addPosts(posts);
});
},
loadNearIndex: function(index) {
// Find the item in the post stream which is nearest to this index. If
// it turns out the be the actual post we're trying to load, then we can
// return a resolved promise (i.e. we don't need to make an API
// request.) Or, if it's a gap, we'll switch on its loading flag.
var item = this.findNearestToIndex(index);
if (item) {
if (! item.gap) {
return Ember.RSVP.resolve([item.get('post')]);
} else {
item.set('direction', 'down').set('loading', true);
}
return this.loadRange(Math.max(item.indexStart, index - 10), item.indexEnd);
}
return Ember.RSVP.reject();
},
addPosts: function(posts) {
this.trigger('postsLoaded', posts);
var stream = this;
posts.forEach(function(post) {
stream.addPost(post);
});
this.trigger('postsAdded');
},
addPost: function(post) {
var stream = this;
var index = this.get('ids').indexOf(post.get('id'));
var content = this.get('content');
// Here we loop through each item in the post stream, and find the gap
// in which this post should be situated. When we find it, we can replace
// it with the post, and new gaps either side if appropriate.
content.some(function(item, i) {
if (item.indexStart <= index && item.indexEnd >= index) {
var newItems = [];
if (item.indexStart < index) {
newItems.push(Ember.Object.create({
gap: true,
indexStart: item.indexStart,
indexEnd: index - 1
}));
}
newItems.push(Ember.Object.create({
indexStart: index,
indexEnd: index,
post: post
}));
if (item.indexEnd > index) {
newItems.push(Ember.Object.create({
gap: true,
indexStart: index + 1,
indexEnd: item.indexEnd
}));
}
content.enumerableContentWillChange();
content.replace(i, 1, newItems);
content.enumerableContentDidChange();
return true;
}
});
},
findNearestToNumber: function(number) {
var nearestItem;
this.get('content').some(function(item) {
var thisNumber = item.get('post.number');
if (thisNumber > number) {
return true;
}
nearestItem = item;
});
return nearestItem;
},
findNearestToIndex: function(index) {
var nearestItem;
this.get('content').some(function(item) {
if (item.indexStart <= index && item.indexEnd >= index) {
nearestItem = item;
return true;
}
});
return nearestItem;
}
});

37
ember/app/models/post.js Normal file
View File

@@ -0,0 +1,37 @@
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Model.extend({
discussion: DS.belongsTo('discussion', {inverse: 'actualPosts'}),
number: DS.attr('number'),
time: DS.attr('string'),
user: DS.belongsTo('user'),
type: DS.attr('string'),
content: DS.attr('string'),
contentHtml: DS.attr('string'),
editTime: DS.attr('string'),
editUser: DS.belongsTo('user'),
edited: Ember.computed.notEmpty('editTime'),
deleteTime: DS.attr('string'),
deleteUser: DS.belongsTo('user'),
deleted: Ember.computed.notEmpty('deleteTime'),
replyTo: DS.belongsTo('post', {inverse: 'replies'}),
replyToNumber: DS.attr('number'),
replyToUser: DS.belongsTo('user'),
replies: DS.hasMany('post', {inverse: 'replyTo'}),
repliesCount: DS.attr('number'),
canEdit: DS.attr('boolean'),
canDelete: DS.attr('boolean'),
likes: function() {
return Math.floor(Math.random() * (Math.random() < 0.3 ? 10 : 1));
}.property()
});

View File

@@ -0,0 +1,44 @@
import Ember from 'ember';
// Represents a collection of results (e.g. a list of discussions)
export default Ember.Object.extend({
// An array of the results.
results: Em.A(),
// The currently-active result.
currentResult: null,
sort: null,
// The index of the currently-active result (determined by ID.) Returns '?'
// if the currently-active result is not in the results list.
index: function() {
var index = '?';
var id = this.get('currentResult.id');
this.get('results').some(function(result, i) {
if (result.get('id') == id) {
index = i + 1;
return true;
}
});
return index;
}.property('currentResult', 'results'),
// The number of results.
count: function() {
return this.get('results.length');
}.property('results'),
// The previous result.
previous: function() {
return this.get('results').objectAt(this.get('index') - 2);
}.property('index'),
// The next result.
next: function() {
return this.get('results').objectAt(this.get('index'));
}.property('index'),
});

21
ember/app/models/user.js Normal file
View File

@@ -0,0 +1,21 @@
import Ember from 'ember';
import DS from 'ember-data';
export default DS.Model.extend({
username: DS.attr('string'),
avatarUrl: DS.attr('string'),
joinTime: DS.attr('date'),
lastSeenTime: DS.attr('date'),
discussionsCount: DS.attr('number'),
postsCount: DS.attr('number'),
canEdit: DS.attr('boolean'),
canDelete: DS.attr('boolean'),
groups: DS.hasMany('group'),
avatarNumber: function() {
return Math.random() > 0.3 ? Math.floor(Math.random() * 19) + 1 : null;
}.property()
});