Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

624 recent updates feed on main page #626

Merged
merged 23 commits into from
Aug 9, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c90f66d
Starting recent updates
chance-stribling Apr 24, 2024
66675ef
working on recent update feed
chance-stribling Apr 29, 2024
8d876dd
thinking about adding marquee
chance-stribling Apr 30, 2024
3febd8f
starting to create the recent updates component
chance-stribling May 14, 2024
4d3d86e
making twitter feed look like observationsfeed
chance-stribling May 17, 2024
f3e839c
working on adding the rest of the images and form
chance-stribling Jun 5, 2024
4eb47fa
forgot the factory
chance-stribling Jun 5, 2024
5312c8b
added logos and migration
chance-stribling Jun 7, 2024
d7631f3
added recent updates, needs to be admin only
chance-stribling Jun 25, 2024
3352296
button only visible when logged in
chance-stribling Jun 26, 2024
563c530
added index and destroy
chance-stribling Jun 27, 2024
c6e5829
edit and delete work, cleaned out WelcomeScene
chance-stribling Jul 1, 2024
e828d4f
edit works, but only after reload
chance-stribling Jul 2, 2024
ed1f733
crud works for updates, need to finish backend
chance-stribling Jul 3, 2024
abe8300
edit should work now
chance-stribling Jul 3, 2024
c12b0cc
added auth and crud should work now
chance-stribling Jul 5, 2024
0ebe46a
moved getTreetFeed to index
chance-stribling Jul 5, 2024
98b8dd9
forgot the route
chance-stribling Jul 5, 2024
b64d493
finished tests for recent updates
chance-stribling Jul 22, 2024
e70dda8
Made corrections to recent updates
chance-stribling Aug 7, 2024
13981b7
added target blank to treet image
chance-stribling Aug 8, 2024
43857be
added route to web-services, created treet test
chance-stribling Aug 8, 2024
4f2a84d
Merge branch 'master' into 624-recent-updates-feed-on-main-page
florence-77 Aug 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/Http/Controllers/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

namespace App\Http\Controllers;

use App\Http\Controllers\Traits\Responds;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use AuthorizesRequests, DispatchesJobs, ValidatesRequests, Responds;
}
92 changes: 92 additions & 0 deletions app/Http/Controllers/TreetController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Treet;

class TreetController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
florence-77 marked this conversation as resolved.
Show resolved Hide resolved
{
//
}

/**
* Show the form for creating a new resource.
*/
public function create(Request $request)
{
florence-77 marked this conversation as resolved.
Show resolved Hide resolved
$treet = Treet::create([
'app_name' => $request->app_name,
'image_path' => $request->image_path,
'description' => $request->description
]);
return $this->created($treet);

}

/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you already have a create method you don't need store, this method can be deleted.

{
//
}

/**
* Display the specified resource.
*/
public function show(Treet $treet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

show is a method that fetches one instance of the model. For example, clicking on a blog post title to view the full post. Since we don't need such a method in this context, this can be deleted.

{
//
}

/**
* Show the form for editing the specified resource.
*/
public function edit(Treet $treet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to be able to edit, but it's not too important at the moment, since we have database access if we really need to edit the text. This method can be deleted.

{
//
}

/**
* Update the specified resource in storage.
*/
public function update(Request $request, Treet $treet)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have a method for update and edit, I would expect those to both perform the same function. You really only need one, if any. Since we don't need this you can just delete this method.

{
//
}

/**
* Remove the specified resource from storage.
*/
public function destroy(Treet $treet)
{
//
}
public function getTreetFeed(Request $request)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code belongs in index(). On that note, take some time to learn about CRUD, a general design model. At a very basic level, the idea is that there are four basic operations we perform on data - Create, Read, Update, Delete. This means one method for each of those operations. In our apps we add one more, a method specifically for fetching multiple, and we call that index(). So, almost all of our controllers contain index(), show(), create(), update(), and delete(). Some purists even argue that if you need more methods than that per controller, you need to refactor! I think that's very extreme. But following the model, most of the time, is helpful I think.

{
$this->validate($request, [
'limit' => 'nullable|integer|min:6|max:90',
]);

$limit = $request->limit ?: 10;

$treets = Treet::select(['id','app_name', 'image_path', 'description', 'created_at'])
->orderBy('created_at', 'desc')
->limit($limit)
->get();

$treets->map(function ($treet) {
$treet->date = $treet->created_at->diffForHumans();
});



return $this->success($treets);
}
}
66 changes: 66 additions & 0 deletions app/Policies/TreetPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace App\Policies;

use App\Treet;
use App\User;
use Illuminate\Auth\Access\Response;

class TreetPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}

/**
* Determine whether the user can view the model.
*/
public function view(User $user, Treet $treet): bool
{
//
}

/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}

/**
* Determine whether the user can update the model.
*/
public function update(User $user, Treet $treet): bool
{
//
}

/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Treet $treet): bool
{
//
}

/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Treet $treet): bool
{
//
}

/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Treet $treet): bool
{
//
}
}
21 changes: 21 additions & 0 deletions app/Treet.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace App;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Treet extends Model
{
use HasFactory;

protected $fillable = [
'app_name',
'image_path',
'description',
];

protected $casts = [
'created_at' => 'datetime',
];
}
15 changes: 15 additions & 0 deletions database/factories/ModelFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,18 @@ function fuzifyCoorinates($original_latitude, $original_longitude)
'observations_count' => $faker->numberBetween(10, 5000),
];
});


$factory->define(\App\Treet::class, function (Faker\Generator $faker) {
$appNames = ['Treesnap','FlorestaDB','HealthyWoods', 'Avid Deer', 'Eastern Forest Pests'];
$imagePaths = ["../images/logos/treesnap_logo.png","../images/logos/florestadb_logo.png","../images/logos/healthywoods_logo.png","../images/logos/aviddeer_logo.png","../images/logos/efp_logo.png"];

$randApp = array_rand($appNames, 1);

return [
'app_name' => $appNames[$randApp],
'image_path' => $imagePaths[$randApp],
'description' => $faker->sentence()
];
});

30 changes: 30 additions & 0 deletions database/migrations/2024_05_14_164032_create_treets_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('treets', function (Blueprint $table) {
$table->id();
$table->text('app_name');
$table->text('image_path');
$table->text('description');
$table->timestamps();
});
}

/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('treets');
}
};
22 changes: 22 additions & 0 deletions database/seeders/TreetSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use App\Treet;


class TreetSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$treets_count = 10;

factory(Treet::class, $treets_count)->create();

}
}
Binary file added public/images/logos/aviddeer_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/logos/efp_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/logos/florestadb_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/logos/healthywoods_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/logos/treesnap_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions resources/assets/js/components/RecentUpdates.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, {Component} from 'react'

export default class RecentUpdates extends Component {
render() {
return (
<div className="recent-updates">
<div className="container">
<h2 className="title is-3 text-white has-text-centered">Recent Updates</h2>
<div className="update-marquee">
<div className="update-card">
<h1>The Hemlock survey has been updated to support the Lingering Hemlock Protocol</h1>
</div>
</div>
</div>
</div>
)
}
}
75 changes: 61 additions & 14 deletions resources/assets/js/components/TwitterFeed.jsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,70 @@
import React from 'react'
import React, { Component } from 'react'

export default class TwitterFeed extends React.Component {
export default class TwitterFeed extends Component {
constructor(props) {
super(props)

this.state = {
treets: [],
loading : true,
}
}

componentDidMount() {
this.interval = setInterval(() => {
if (window.twttr) {
window.twttr.widgets.load()
clearInterval(this.interval)
}
}, 15)
this.loadTreets()

setInterval(this.loadTreets.bind(this), 120000)
}

loadTreets() {
axios.get(`/web/treets/feed`).then(response => {
this.setState({treets: response.data.data, loading: false})
}).catch(error => {
console.log(error)
this.setState({loading: false})
})
}

renderTreet(treet) {
return (

<div key={treet.id}
className={'item-box elevation-1 is-lighter-dark is-flex flex-space-between flex-v-center'}>
<div className="is-flex flex-v-center flex-column-left flex-wrap">
<div className="flex-row">

<div className="item mr-3">
<img src={treet.image_path}
alt={treet.app_name}
className="item-thumbnail "
style={{marginTop: 8}}/>
</div>
<div className="item">
<div className="text-dark-muted text-wrap"><strong>{treet.app_name}</strong></div>
</div>
</div>
<div className="item">

<div className="text-dark-muted my-4 text-wrap w-100">{treet.description}</div>
<div className="text-dark-muted text-wrap">{treet.date}</div>
</div>
</div>
</div>

)
}
render() {
return (
<div>
<a className="twitter-timeline"
data-height="487"
data-width="432"
data-link-color="#2A9D8F"
href="https://twitter.com/Treesnapapp?ref_src=twsrc%5Etfw">Tweets by Treesnapapp</a>
<div style={{maxHeight: 487, overflowY: 'auto'}} className={'invisible-scrollbar'}>
{this.state.loading ?
<p className="has-text-centered">
<i className="fa fa-spinner fa-spin"></i>
</p>
: null}
{this.state.treets.map(this.renderTreet.bind(this))}
{this.state.treets.length === 0 && !this.state.loading ?
<p className="text-dark-muted has-text-centered">There are no treets at this time</p>
: null}
</div>
)
}
Expand Down
Loading