Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrlacey/1cd1f7a0049c69eb774f to your computer and use it in GitHub Desktop.
Save mrlacey/1cd1f7a0049c69eb774f to your computer and use it in GitHub Desktop.
Twitter in App Studio Apps
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Windows.Web.Http;
using Windows.Web.Http.Headers;
using Newtonsoft.Json;
namespace AppStudio.Data
{
public class TwitterDataProvider
{
private readonly string _userName;
private const string UserAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
private string authString;
public TwitterDataProvider(string userName, string consumerKey, string consumerSecret)
{
_userName = userName;
authString = Convert.ToBase64String(Encoding.UTF8.GetBytes(
Uri.EscapeDataString(consumerKey) +
":" +
Uri.EscapeDataString(consumerSecret)
));
}
public async Task<ObservableCollection<TwitterSchema>> Load()
{
var accessToken = await this.GetAccessToken(this.authString);
var timelineJson = await this.GetTimeline(accessToken, _userName);
var tweets = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(timelineJson);
var result = new ObservableCollection<TwitterSchema>();
foreach (var tweet in tweets)
{
result.Add(new TwitterSchema
{
Id = tweet["id"].ToString(),
Content = tweet["text"].ToString(),
CreatedDate = DateTimeOffset.ParseExact(tweet["created_at"].ToString(),
"ddd MMM dd HH:mm:ss zzz yyyy",
CultureInfo.InvariantCulture)
});
}
return result;
}
private async Task<string> GetAccessToken(string authData)
{
var oauth_url = "https://api.twitter.com/oauth2/token";
var post_data = "grant_type=client_credentials";
var client = new Windows.Web.Http.HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent);
client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Basic", authData);
var body = new HttpStringContent(post_data);
body.Headers.ContentType = new HttpMediaTypeHeaderValue("application/x-www-form-urlencoded;charset=UTF-8");
var authResp = await client.PostAsync(new Uri(oauth_url), body);
var authRespStr = await authResp.Content.ReadAsStringAsync();
var item = JsonConvert.DeserializeObject<Dictionary<string, string>>(authRespStr);
string access_token = item["access_token"];
return access_token;
}
private async Task<string> GetTimeline(string access_token, string userName)
{
var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd(UserAgent);
client.DefaultRequestHeaders.Authorization = new HttpCredentialsHeaderValue("Bearer", access_token);
var resp = await client.GetAsync(new Uri("https://api.twitter.com/1.1/statuses/user_timeline.json?count=25&trim_user=1&screen_name=" + userName));
var strResp = await resp.Content.ReadAsStringAsync();
dynamic tweets = JsonConvert.DeserializeObject(strResp);
return JsonConvert.SerializeObject(tweets, Formatting.Indented);
}
}
}
using System;
namespace AppStudio.Data
{
public class TwitterSchema : BindableSchemaBase, IEquatable<TwitterSchema>
{
private string _content;
private DateTimeOffset _createdDate;
public string Content
{
get { return _content; }
set { SetProperty(ref _content, value); }
}
public DateTimeOffset CreatedDate
{
get { return _createdDate; }
set { SetProperty(ref _createdDate, value); }
}
public string FormattedCreatedDate
{
get
{
return _createdDate.ToString("h:mmtt").ToLowerInvariant() + _createdDate.ToString(" - d MMM yy");
}
}
public override string DefaultImageUrl
{
get
{
// We don't have an image for each tweet but the cache requires one
return string.Empty;
}
}
public override string DefaultContent
{
get { return Content; }
}
override public string GetValue(string fieldName)
{
if (!String.IsNullOrEmpty(fieldName))
{
switch (fieldName.ToLower())
{
case "id":
return String.Format("{0}", Id);
case "content":
return String.Format("{0}", Content);
case "createdDate":
return String.Format("{0}", CreatedDate);
case "defaulttitle":
return String.Format("{0}", DefaultTitle);
case "defaultsummary":
return String.Format("{0}", DefaultSummary);
case "defaultimageurl":
return String.Format("{0}", DefaultImageUrl);
default:
break;
}
}
return String.Empty;
}
public override string DefaultTitle
{
get { return this.Content; }
}
public override string DefaultSummary
{
get { return this.Content; }
}
public bool Equals(TwitterSchema other)
{
if (other == null)
{
return false;
}
return this.Id == other.Id;
}
}
}
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace AppStudio.Data
{
public class TwitterDataSource : DataSourceBase<TwitterSchema>
{
// TODO: update these values as appropriate
private const string _username = "mrlacey";
private const string _consumerKey = "CONSUMER_KEY";
private const string _consumerSecret = "CONSUMER_SECRET";
protected override string CacheKey
{
get { return "TwitterDataSource"; }
}
public override bool HasStaticData
{
get { return false; }
}
public static string UserName { get { return _username; } }
public async override Task<IEnumerable<TwitterSchema>> LoadDataAsync()
{
try
{
var twitterDataProvider = new TwitterDataProvider(_username, _consumerKey, _consumerSecret);
return await twitterDataProvider.Load();
}
catch (Exception ex)
{
AppLogs.WriteError("TwitterDataSourceDataSource.LoadData", ex.ToString());
return new TwitterSchema[0];
}
}
}
}
<!-- add this ResourceDictionary -->
<ResourceDictionary Source="Views/DataTemplates/TwitterViews.xaml"/>
// add this field
private TwitterViewModel _twitterModel;
// add this property
public TwitterViewModel TwitterModel
{
get { return _twitterModel ?? (_twitterModel = new TwitterViewModel()); }
}
// Add this to the method SetViewType
TwitterModel.ViewType = viewType;
// add this to the Task array in the method LoadDataAsync
TwitterModel.LoadItemsAsync(forceRefresh),
using System;
using Windows.System;
using Windows.UI.Xaml;
using AppStudio.Data;
namespace AppStudio.ViewModels
{
public class TwitterViewModel : ViewModelBase<TwitterSchema>
{
private RelayCommandEx<TwitterSchema> itemClickCommand;
public RelayCommandEx<TwitterSchema> ItemClickCommand
{
get
{
if (itemClickCommand == null)
{
itemClickCommand = new RelayCommandEx<TwitterSchema>(
async (item) =>
{
await Launcher.LaunchUriAsync(new Uri(string.Format("https://twitter.com/{0}/status/{1}", TwitterDataSource.UserName, item.Id)));
});
}
return itemClickCommand;
}
}
override protected DataSourceBase<TwitterSchema> CreateDataSource()
{
return new TwitterDataSource();
}
override public Visibility GoToSourceVisibility
{
get { return ViewType == ViewTypes.Detail ? Visibility.Visible : Visibility.Collapsed; }
}
override protected void GoToSource()
{
base.GoToSource("{FeedUrl}");
}
override public Visibility RefreshVisibility
{
get { return ViewType == ViewTypes.List ? Visibility.Visible : Visibility.Collapsed; }
}
public override bool HasMoreItems
{
get { return false; }
}
override public void NavigateToSectionList()
{
throw new NotImplementedException();
}
override protected void NavigateToSelectedItem()
{
throw new NotImplementedException();
}
}
}
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:commands="using:AppStudio.Commands">
<DataTemplate x:Key="TwitterList">
<Grid>
<ListView
ItemsSource="{Binding Items}"
SelectionMode="None"
IsSwipeEnabled="False"
IsItemClickEnabled="True"
commands:ItemClickCommand.Command="{Binding ItemClickCommand}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemTemplate="{StaticResource TwitterItem}" />
<ProgressBar Width="480" Height="40" Foreground="White" VerticalAlignment="Top" IsIndeterminate="True" Visibility="{Binding ProgressBarVisibility}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="TwitterItem">
<StackPanel MaxWidth="480" Margin="0,0,0,15">
<TextBlock Style="{StaticResource ItemHeaderWrapText}"
FontWeight="Normal"
VerticalAlignment="Top"
Text="{Binding Content, Converter={StaticResource TextPlainConverter}, ConverterParameter=140}" />
<TextBlock Style="{StaticResource ItemSubheaderText}"
VerticalAlignment="Top"
Text="{Binding FormattedCreatedDate}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
// add this to your hub
<HubSection x:Name="TwitterSection"
Header="Twitter"
DataContext="{Binding MainViewModel.TwitterModel}"
ContentTemplate="{StaticResource TwitterList}"
IsHeaderInteractive="{Binding HasMoreItems}"
Style="{StaticResource AppHubSectionStyle}" />
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:AppStudio.Controls"
xmlns:html2xaml="using:AppStudio.Controls.Html2Xaml"
xmlns:commands="using:AppStudio.Commands">
<DataTemplate x:Key="TwitterList">
<Grid>
<ListView
ItemsSource="{Binding Items}"
SelectionMode="None"
IsSwipeEnabled="False"
IsItemClickEnabled="True"
commands:ItemClickCommand.Command="{Binding ItemClickCommand}"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ItemTemplate="{StaticResource TwitterItem}" />
<ProgressBar Width="380" Height="40" Foreground="White" VerticalAlignment="Top" IsIndeterminate="True" Visibility="{Binding ProgressBarVisibility}" />
</Grid>
</DataTemplate>
<DataTemplate x:Key="TwitterItem">
<StackPanel MaxWidth="380" Margin="0,0,0,15">
<TextBlock Style="{StaticResource ItemHeaderWrapText}"
FontWeight="Normal"
VerticalAlignment="Top"
Text="{Binding Content, Converter={StaticResource TextPlainConverter}, ConverterParameter=140}" />
<TextBlock Style="{StaticResource ItemSubheaderText}"
VerticalAlignment="Top"
Text="{Binding FormattedCreatedDate}" />
</StackPanel>
</DataTemplate>
</ResourceDictionary>
// add this to your hub
<HubSection HeaderTemplate="{StaticResource HubHeader}"
Width="360"
Header="Twitter"
DataContext="{Binding MainViewModel.TwitterModel}"
ContentTemplate="{StaticResource TwitterList}"
IsHeaderInteractive="{Binding HasMoreItems}" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment